bug-gnulib
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: canonicalize_file_name does not support MS-Windows style file names


From: Paul Eggert
Subject: Re: canonicalize_file_name does not support MS-Windows style file names
Date: Tue, 20 Nov 2012 13:49:09 -0800
User-agent: Mozilla/5.0 (X11; Linux x86_64; rv:16.0) Gecko/20121029 Thunderbird/16.0.2

Thanks, I made some trivial changes to the patch to get it to
compile and use gnulib style and installed the following into gnulib:

>From 169ed317dc19a14160656868b08f3ba409f1a87f Mon Sep 17 00:00:00 2001
From: Eli Zaretskii <address@hidden>
Date: Tue, 20 Nov 2012 13:47:22 -0800
Subject: [PATCH] canonicalize, canonicalize-lgpl: support MS-Windows file
 names

See <http://lists.gnu.org/archive/html/bug-gnulib/2012-11/msg00074.html>
for test cases, which it'd be nice to add at some point.
* lib/canonicalize.c, lib/canonicalize-lgpl.c: Include dosname.h.
* lib/canonicalize.c (canonicalize_filename_mode):
* lib/canonicalize-lgpl.c (__realpath):
Use FILE_SYSTEM_PREFIX_LEN instead of assuming that the first
slash is at the beginning of the file name.  Use ISSLASH, instead
of a literal '/'.  Use IS_ABSOLUTE_FILE_NAME instead of comparing
the first character with '/'.  Test for
DOUBLE_SLASH_IS_DISTINCT_ROOT only if the file name does not begin
with a drive letter.
* lib/canonicalize.c (SLASHES): New macro.
(canonicalize_filename_mode): Use SLASHES instead of a literal "/".
---
 ChangeLog               | 17 +++++++++++++
 lib/canonicalize-lgpl.c | 65 ++++++++++++++++++++++++++++++----------------
 lib/canonicalize.c      | 68 ++++++++++++++++++++++++++++++++++---------------
 3 files changed, 107 insertions(+), 43 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 348e0d5..9c2555e 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,20 @@
+2012-11-20  Eli Zaretskii  <address@hidden>
+
+       canonicalize, canonicalize-lgpl: support MS-Windows file names
+       See <http://lists.gnu.org/archive/html/bug-gnulib/2012-11/msg00074.html>
+       for test cases, which it'd be nice to add at some point.
+       * lib/canonicalize.c, lib/canonicalize-lgpl.c: Include dosname.h.
+       * lib/canonicalize.c (canonicalize_filename_mode):
+       * lib/canonicalize-lgpl.c (__realpath):
+       Use FILE_SYSTEM_PREFIX_LEN instead of assuming that the first
+       slash is at the beginning of the file name.  Use ISSLASH, instead
+       of a literal '/'.  Use IS_ABSOLUTE_FILE_NAME instead of comparing
+       the first character with '/'.  Test for
+       DOUBLE_SLASH_IS_DISTINCT_ROOT only if the file name does not begin
+       with a drive letter.
+       * lib/canonicalize.c (SLASHES): New macro.
+       (canonicalize_filename_mode): Use SLASHES instead of a literal "/".
+
 2012-11-17  Dmitry V. Levin  <address@hidden>
 
        fts: introduce FTS_VERBATIM
diff --git a/lib/canonicalize-lgpl.c b/lib/canonicalize-lgpl.c
index 7aa2d92..0888501 100644
--- a/lib/canonicalize-lgpl.c
+++ b/lib/canonicalize-lgpl.c
@@ -51,6 +51,7 @@
 # define __realpath realpath
 # include "pathmax.h"
 # include "malloca.h"
+# include "dosname.h"
 # if HAVE_GETCWD
 #  if IN_RELOCWRAPPER
     /* When building the relocatable program wrapper, use the system's getcwd
@@ -101,6 +102,7 @@ __realpath (const char *name, char *resolved)
   const char *start, *end, *rpath_limit;
   long int path_max;
   int num_links = 0;
+  size_t prefix_len;
 
   if (name == NULL)
     {
@@ -143,7 +145,11 @@ __realpath (const char *name, char *resolved)
     rpath = resolved;
   rpath_limit = rpath + path_max;
 
-  if (name[0] != '/')
+  /* This is always zero for Posix hosts, but can be 2 for MS-Windows
+     and MS-DOS X:/foo/bar file names.  */
+  prefix_len = FILE_SYSTEM_PREFIX_LEN (name);
+
+  if (!IS_ABSOLUTE_FILE_NAME (name))
     {
       if (!__getcwd (rpath, path_max))
         {
@@ -154,17 +160,22 @@ __realpath (const char *name, char *resolved)
     }
   else
     {
-      rpath[0] = '/';
-      dest = rpath + 1;
+      dest = rpath;
+      if (prefix_len)
+        {
+          memcpy (rpath, name, prefix_len);
+          dest += prefix_len;
+        }
+      *dest++ = '/';
       if (DOUBLE_SLASH_IS_DISTINCT_ROOT)
         {
-          if (name[1] == '/' && name[2] != '/')
+          if (ISSLASH (name[1]) && !ISSLASH (name[2]) && !prefix_len)
             *dest++ = '/';
           *dest = '\0';
         }
     }
 
-  for (start = end = name; *start; start = end)
+  for (start = end = name + prefix_len; *start; start = end)
     {
 #ifdef _LIBC
       struct stat64 st;
@@ -174,11 +185,11 @@ __realpath (const char *name, char *resolved)
       int n;
 
       /* Skip sequence of multiple path-separators.  */
-      while (*start == '/')
+      while (ISSLASH (*start))
         ++start;
 
       /* Find end of path component.  */
-      for (end = start; *end && *end != '/'; ++end)
+      for (end = start; *end && !ISSLASH (*end); ++end)
         /* Nothing.  */;
 
       if (end - start == 0)
@@ -188,17 +199,19 @@ __realpath (const char *name, char *resolved)
       else if (end - start == 2 && start[0] == '.' && start[1] == '.')
         {
           /* Back up to previous component, ignore if at root already.  */
-          if (dest > rpath + 1)
-            while ((--dest)[-1] != '/');
-          if (DOUBLE_SLASH_IS_DISTINCT_ROOT && dest == rpath + 1
-              && *dest == '/' && dest[1] != '/')
+          if (dest > rpath + prefix_len + 1)
+            for (--dest; !ISSLASH (dest[-1]); --dest)
+              continue;
+          if (DOUBLE_SLASH_IS_DISTINCT_ROOT
+              && dest == rpath + 1 && !prefix_len
+              && ISSLASH (*dest) && !ISSLASH (dest[1]))
             dest++;
         }
       else
         {
           size_t new_size;
 
-          if (dest[-1] != '/')
+          if (!ISSLASH (dest[-1]))
             *dest++ = '/';
 
           if (dest + (end - start) >= rpath_limit)
@@ -209,7 +222,7 @@ __realpath (const char *name, char *resolved)
               if (resolved)
                 {
                   __set_errno (ENAMETOOLONG);
-                  if (dest > rpath + 1)
+                  if (dest > rpath + prefix_len + 1)
                     dest--;
                   *dest = '\0';
                   goto error;
@@ -299,24 +312,32 @@ __realpath (const char *name, char *resolved)
               memmove (&extra_buf[n], end, len + 1);
               name = end = memcpy (extra_buf, buf, n);
 
-              if (buf[0] == '/')
+              if (IS_ABSOLUTE_FILE_NAME (buf))
                 {
-                  dest = rpath + 1;     /* It's an absolute symlink */
+                  size_t pfxlen = FILE_SYSTEM_PREFIX_LEN (buf);
+
+                  if (pfxlen)
+                    memcpy (rpath, buf, pfxlen);
+                  dest = rpath + pfxlen;
+                  *dest++ = '/'; /* It's an absolute symlink */
                   if (DOUBLE_SLASH_IS_DISTINCT_ROOT)
                     {
-                      if (buf[1] == '/' && buf[2] != '/')
+                      if (ISSLASH (buf[1]) && !ISSLASH (buf[2]) && !pfxlen)
                         *dest++ = '/';
                       *dest = '\0';
                     }
+                  /* Install the new prefix to be in effect hereafter.  */
+                  prefix_len = pfxlen;
                 }
               else
                 {
                   /* Back up to previous component, ignore if at root
                      already: */
-                  if (dest > rpath + 1)
-                    while ((--dest)[-1] != '/');
+                  if (dest > rpath + prefix_len + 1)
+                    for (--dest; !ISSLASH (dest[-1]); --dest)
+                      continue;
                   if (DOUBLE_SLASH_IS_DISTINCT_ROOT && dest == rpath + 1
-                      && *dest == '/' && dest[1] != '/')
+                      && ISSLASH (*dest) && !ISSLASH (dest[1]) && !prefix_len)
                     dest++;
                 }
             }
@@ -327,10 +348,10 @@ __realpath (const char *name, char *resolved)
             }
         }
     }
-  if (dest > rpath + 1 && dest[-1] == '/')
+  if (dest > rpath + prefix_len + 1 && ISSLASH (dest[-1]))
     --dest;
-  if (DOUBLE_SLASH_IS_DISTINCT_ROOT && dest == rpath + 1
-      && *dest == '/' && dest[1] != '/')
+  if (DOUBLE_SLASH_IS_DISTINCT_ROOT && dest == rpath + 1 && !prefix_len
+      && ISSLASH (*dest) && !ISSLASH (dest[1]))
     dest++;
   *dest = '\0';
 
diff --git a/lib/canonicalize.c b/lib/canonicalize.c
index 20ca40b..33ad29f 100644
--- a/lib/canonicalize.c
+++ b/lib/canonicalize.c
@@ -30,6 +30,7 @@
 #include "pathmax.h"
 #include "xalloc.h"
 #include "xgetcwd.h"
+#include "dosname.h"
 
 #define MULTIPLE_BITS_SET(i) (((i) & ((i) - 1)) != 0)
 
@@ -43,6 +44,12 @@
 # define DOUBLE_SLASH_IS_DISTINCT_ROOT 0
 #endif
 
+#if ISSLASH ('\\')
+# define SLASHES "/\\"
+#else
+# define SLASHES "/"
+#endif
+
 #if !((HAVE_CANONICALIZE_FILE_NAME && FUNC_REALPATH_WORKS)      \
       || GNULIB_CANONICALIZE_LGPL)
 /* Return the canonical absolute name of file NAME.  A canonical name
@@ -100,6 +107,7 @@ canonicalize_filename_mode (const char *name, 
canonicalize_mode_t can_mode)
   int saved_errno;
   int can_flags = can_mode & ~CAN_MODE_MASK;
   bool logical = can_flags & CAN_NOLINKS;
+  size_t prefix_len;
 
   can_mode &= CAN_MODE_MASK;
 
@@ -121,7 +129,11 @@ canonicalize_filename_mode (const char *name, 
canonicalize_mode_t can_mode)
       return NULL;
     }
 
-  if (name[0] != '/')
+  /* This is always zero for Posix hosts, but can be 2 for MS-Windows
+     and MS-DOS X:/foo/bar file names.  */
+  prefix_len = FILE_SYSTEM_PREFIX_LEN (name);
+
+  if (!IS_ABSOLUTE_FILE_NAME (name))
     {
       rname = xgetcwd ();
       if (!rname)
@@ -143,24 +155,29 @@ canonicalize_filename_mode (const char *name, 
canonicalize_mode_t can_mode)
     {
       rname = xmalloc (PATH_MAX);
       rname_limit = rname + PATH_MAX;
-      rname[0] = '/';
-      dest = rname + 1;
+      dest = rname;
+      if (prefix_len)
+        {
+          memcpy (rname, name, prefix_len);
+          dest += prefix_len;
+        }
+      *dest++ = '/';
       if (DOUBLE_SLASH_IS_DISTINCT_ROOT)
         {
-          if (name[1] == '/' && name[2] != '/')
+          if (ISSLASH (name[1]) && !ISSLASH (name[2]) && !prefix_len)
             *dest++ = '/';
           *dest = '\0';
         }
     }
 
-  for (start = name; *start; start = end)
+  for (start = name + prefix_len; *start; start = end)
     {
       /* Skip sequence of multiple file name separators.  */
-      while (*start == '/')
+      while (ISSLASH (*start))
         ++start;
 
       /* Find end of component.  */
-      for (end = start; *end && *end != '/'; ++end)
+      for (end = start; *end && !ISSLASH (*end); ++end)
         /* Nothing.  */;
 
       if (end - start == 0)
@@ -170,17 +187,18 @@ canonicalize_filename_mode (const char *name, 
canonicalize_mode_t can_mode)
       else if (end - start == 2 && start[0] == '.' && start[1] == '.')
         {
           /* Back up to previous component, ignore if at root already.  */
-          if (dest > rname + 1)
-            while ((--dest)[-1] != '/');
+          if (dest > rname + prefix_len + 1)
+            for (--dest; !ISSLASH (dest[-1]); --dest)
+              continue;
           if (DOUBLE_SLASH_IS_DISTINCT_ROOT && dest == rname + 1
-              && *dest == '/' && dest[1] != '/')
+              && !prefix_len && ISSLASH (*dest) && !ISSLASH (dest[1]))
             dest++;
         }
       else
         {
           struct stat st;
 
-          if (dest[-1] != '/')
+          if (!ISSLASH (dest[-1]))
             *dest++ = '/';
 
           if (dest + (end - start) >= rname_limit)
@@ -216,7 +234,7 @@ canonicalize_filename_mode (const char *name, 
canonicalize_mode_t can_mode)
                 goto error;
               if (can_mode == CAN_ALL_BUT_LAST)
                 {
-                  if (end[strspn (end, "/")] || saved_errno != ENOENT)
+                  if (end[strspn (end, SLASHES)] || saved_errno != ENOENT)
                     goto error;
                   continue;
                 }
@@ -268,24 +286,32 @@ canonicalize_filename_mode (const char *name, 
canonicalize_mode_t can_mode)
               memmove (&extra_buf[n], end, len + 1);
               name = end = memcpy (extra_buf, buf, n);
 
-              if (buf[0] == '/')
+              if (IS_ABSOLUTE_FILE_NAME (buf))
                 {
-                  dest = rname + 1;     /* It's an absolute symlink */
+                  size_t pfxlen = FILE_SYSTEM_PREFIX_LEN (buf);
+
+                  if (pfxlen)
+                    memcpy (rname, buf, pfxlen);
+                  dest = rname + pfxlen;
+                  *dest++ = '/'; /* It's an absolute symlink */
                   if (DOUBLE_SLASH_IS_DISTINCT_ROOT)
                     {
-                      if (buf[1] == '/' && buf[2] != '/')
+                      if (ISSLASH (buf[1]) && !ISSLASH (buf[2]) && !pfxlen)
                         *dest++ = '/';
                       *dest = '\0';
                     }
+                  /* Install the new prefix to be in effect hereafter.  */
+                  prefix_len = pfxlen;
                 }
               else
                 {
                   /* Back up to previous component, ignore if at root
                      already: */
-                  if (dest > rname + 1)
-                    while ((--dest)[-1] != '/');
+                  if (dest > rname + prefix_len + 1)
+                    for (--dest; !ISSLASH (dest[-1]); --dest)
+                      continue;
                   if (DOUBLE_SLASH_IS_DISTINCT_ROOT && dest == rname + 1
-                      && *dest == '/' && dest[1] != '/')
+                      && ISSLASH (*dest) && !ISSLASH (dest[1]) && !prefix_len)
                     dest++;
                 }
 
@@ -301,10 +327,10 @@ canonicalize_filename_mode (const char *name, 
canonicalize_mode_t can_mode)
             }
         }
     }
-  if (dest > rname + 1 && dest[-1] == '/')
+  if (dest > rname + prefix_len + 1 && ISSLASH (dest[-1]))
     --dest;
-  if (DOUBLE_SLASH_IS_DISTINCT_ROOT && dest == rname + 1
-      && *dest == '/' && dest[1] != '/')
+  if (DOUBLE_SLASH_IS_DISTINCT_ROOT && dest == rname + 1 && !prefix_len
+      && ISSLASH (*dest) && !ISSLASH (dest[1]))
     dest++;
   *dest = '\0';
   if (rname_limit != dest + 1)
-- 
1.7.11.7




reply via email to

[Prev in Thread] Current Thread [Next in Thread]