I found a bug, so I'm attaching a patch to fix it. The list of test cases is now this:
'foo.bar' => 'd:\usr\eli\utils\lib/foo.bar' '.\foo.bar' => 'd:\usr\eli\utils\lib/foo.bar' './foo.bar' => 'd:\usr\eli\utils\lib/foo.bar' '..\foo.bar' => 'd:\usr\eli\utils\foo.bar' '../foo.bar' => 'd:\usr\eli\utils\foo.bar' '../CURDIR/foo.bar' => 'd:\usr\eli\utils\CURDIR/foo.bar' '..\CURDIR\foo.bar' => 'd:\usr\eli\utils\CURDIR/foo.bar' '..\CURDIR/foo.bar' => 'd:\usr\eli\utils\CURDIR/foo.bar' '..\CURDIR\foo.bar\' => '(null)' '../CURDIR\foo.bar' => 'd:\usr\eli\utils\CURDIR/foo.bar' 'C:WINDOWS' => '(null)' 'C:system32' => '(null)' 'C:/WINDOWS' => 'C:/WINDOWS' 'C:\WINDOWS' => 'C:/WINDOWS' 'C:\\WINDOWS' => 'C:/WINDOWS' 'C:./WINDOWS' => '(null)' 'C:./system32' => '(null)' 'C:.\WINDOWS' => '(null)' 'C:.\system32' => '(null)' 'C:.' => 'd:\usr\eli\utils\lib' 'C:/' => 'C:/' 'C:\' => 'C:/' Here's the patch relative to the latest gnulib version: 2012-11-21 Eli Zaretskii <e...@gnu.org> * canonicalize-lgpl.c (__realpath): Recompute prefix_len after fetching the current directory. Don't overrun the beginning of rpath if there's no slashes after the MS-Windows drive letter. * canonicalize.c (canonicalize_filename_mode): Recompute prefix_len after fetching the current directory. Don't overrun the beginning of rname if there's no slashes after the MS-Windows drive letter. --- canonicalize-lgpl.c~1 2012-11-21 06:45:02.000000000 +0200 +++ canonicalize-lgpl.c 2012-11-21 07:50:41.626852500 +0200 @@ -157,6 +157,8 @@ __realpath (const char *name, char *reso goto error; } dest = strchr (rpath, '\0'); + start = name; + prefix_len = FILE_SYSTEM_PREFIX_LEN (rpath); } else { @@ -173,9 +175,10 @@ __realpath (const char *name, char *reso *dest++ = '/'; *dest = '\0'; } + start = name + prefix_len; } - for (start = end = name + prefix_len; *start; start = end) + for (end = start; *start; start = end) { #ifdef _LIBC struct stat64 st; @@ -200,7 +203,7 @@ __realpath (const char *name, char *reso { /* Back up to previous component, ignore if at root already. */ if (dest > rpath + prefix_len + 1) - for (--dest; !ISSLASH (dest[-1]); --dest) + for (--dest; dest > rpath && !ISSLASH (dest[-1]); --dest) continue; if (DOUBLE_SLASH_IS_DISTINCT_ROOT && dest == rpath + 1 && !prefix_len @@ -334,7 +337,7 @@ __realpath (const char *name, char *reso /* Back up to previous component, ignore if at root already: */ if (dest > rpath + prefix_len + 1) - for (--dest; !ISSLASH (dest[-1]); --dest) + for (--dest; dest > rpath && !ISSLASH (dest[-1]); --dest) continue; if (DOUBLE_SLASH_IS_DISTINCT_ROOT && dest == rpath + 1 && ISSLASH (*dest) && !ISSLASH (dest[1]) && !prefix_len) --- canonicalize.c~1 2012-11-21 06:45:02.000000000 +0200 +++ canonicalize.c 2012-11-21 07:52:42.324052500 +0200 @@ -150,6 +150,8 @@ canonicalize_filename_mode (const char * { rname_limit = dest; } + start = name; + prefix_len = FILE_SYSTEM_PREFIX_LEN (rname); } else { @@ -168,9 +170,10 @@ canonicalize_filename_mode (const char * *dest++ = '/'; *dest = '\0'; } + start = name + prefix_len; } - for (start = name + prefix_len; *start; start = end) + for ( ; *start; start = end) { /* Skip sequence of multiple file name separators. */ while (ISSLASH (*start)) @@ -188,7 +191,7 @@ canonicalize_filename_mode (const char * { /* Back up to previous component, ignore if at root already. */ if (dest > rname + prefix_len + 1) - for (--dest; !ISSLASH (dest[-1]); --dest) + for (--dest; dest > rname && !ISSLASH (dest[-1]); --dest) continue; if (DOUBLE_SLASH_IS_DISTINCT_ROOT && dest == rname + 1 && !prefix_len && ISSLASH (*dest) && !ISSLASH (dest[1])) @@ -308,7 +311,7 @@ canonicalize_filename_mode (const char * /* Back up to previous component, ignore if at root already: */ if (dest > rname + prefix_len + 1) - for (--dest; !ISSLASH (dest[-1]); --dest) + for (--dest; dest > rname && !ISSLASH (dest[-1]); --dest) continue; if (DOUBLE_SLASH_IS_DISTINCT_ROOT && dest == rname + 1 && ISSLASH (*dest) && !ISSLASH (dest[1]) && !prefix_len)