You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by rh...@apache.org on 2010/06/25 13:31:42 UTC

svn commit: r957893 - in /subversion/trunk/subversion: include/svn_dirent_uri.h libsvn_ra_local/split_url.c libsvn_subr/dirent_uri.c tests/libsvn_subr/dirent_uri-test.c

Author: rhuijben
Date: Fri Jun 25 11:31:41 2010
New Revision: 957893

URL: http://svn.apache.org/viewvc?rev=957893&view=rev
Log:
Move the knowledge on how file:/// urls are handled out of libsvn_ra_local,
into libsvn_subr's dirent_uri.c to allow managing and testing all the
uri<->dirent translations in one place.

* subversion/include/svn_dirent_uri.h
  (svn_uri_get_dirent_from_file_url): New function.

* subversion/libsvn_ra_local/split_url.c
  (includes): Add svn_dirent_uri.h.
  (svn_ra_local__split_URL): Use new svn_uri_get_dirent_from_file_url
    function and calculate the url and fs path from the result of this
    function.

* subversion/libsvn_subr/dirent_uri.c
  (svn_uri_get_dirent_from_file_url): New function, based on
    svn_ra_local__split_URL. Fix some errors on handling drives and unc
    paths on Windows, found by adding some tests. Also remove an
    obsolete comment on how UNC could be used a long time ago, since that
    support is broken since at least 1.5.

* subversion/tests/libsvn_subr/dirent_uri-test.c
  (test_dirent_from_file_url): New function.
  (test_funcs): Add test_dirent_from_file_url.

Modified:
    subversion/trunk/subversion/include/svn_dirent_uri.h
    subversion/trunk/subversion/libsvn_ra_local/split_url.c
    subversion/trunk/subversion/libsvn_subr/dirent_uri.c
    subversion/trunk/subversion/tests/libsvn_subr/dirent_uri-test.c

Modified: subversion/trunk/subversion/include/svn_dirent_uri.h
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/include/svn_dirent_uri.h?rev=957893&r1=957892&r2=957893&view=diff
==============================================================================
--- subversion/trunk/subversion/include/svn_dirent_uri.h (original)
+++ subversion/trunk/subversion/include/svn_dirent_uri.h Fri Jun 25 11:31:41 2010
@@ -779,6 +779,16 @@ svn_dirent_is_under_root(char **full_pat
                          const char *path,
                          apr_pool_t *pool);
 
+/* Converts a file:// url into a proper dirent by using the platform specific
+ * files:// rules.
+ *
+ * @since New in 1.7.
+ */
+svn_error_t *
+svn_uri_get_dirent_from_file_url(const char **dirent,
+                                 const char *url,
+                                 apr_pool_t *pool);
+
 #ifdef __cplusplus
 }
 #endif /* __cplusplus */

Modified: subversion/trunk/subversion/libsvn_ra_local/split_url.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_ra_local/split_url.c?rev=957893&r1=957892&r2=957893&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_ra_local/split_url.c (original)
+++ subversion/trunk/subversion/libsvn_ra_local/split_url.c Fri Jun 25 11:31:41 2010
@@ -24,6 +24,7 @@
 #include "ra_local.h"
 #include <string.h>
 #include "svn_path.h"
+#include "svn_dirent_uri.h"
 #include "svn_private_config.h"
 
 
@@ -35,129 +36,30 @@ svn_ra_local__split_URL(svn_repos_t **re
                         apr_pool_t *pool)
 {
   svn_error_t *err = SVN_NO_ERROR;
-  const char *repos_root;
-  const char *hostname, *path;
+  const char *repos_dirent;
+  const char *repos_root_dirent;
   svn_stringbuf_t *urlbuf;
 
   /* Verify that the URL is well-formed (loosely) */
-
-  /* First, check for the "file://" prefix. */
+  /* First, check for the "file://" prefix, to avoid assertion in tests */
   if (strncmp(URL, "file://", 7) != 0)
-    return svn_error_createf
-      (SVN_ERR_RA_ILLEGAL_URL, NULL,
-       _("Local URL '%s' does not contain 'file://' prefix"), URL);
-
-  /* Then, skip what's between the "file://" prefix and the next
-     occurance of '/' -- this is the hostname, and we are considering
-     everything from that '/' until the end of the URL to be the
-     absolute path portion of the URL.
-     If we got just "file://", treat it the same as "file:///". */
-  hostname = URL + 7;
-  if (*hostname == '\0')
-    {
-      path = "/";
-      hostname = NULL;
-    }
-  else
-    {
-      path = strchr(hostname, '/');
-      if (! path)
-        return svn_error_createf
-          (SVN_ERR_RA_ILLEGAL_URL, NULL,
-           _("Local URL '%s' contains only a hostname, no path"), URL);
-
-      /* Treat localhost as an empty hostname. */
-      if (hostname != path)
-        {
-          hostname = svn_path_uri_decode(apr_pstrmemdup(pool, hostname,
-                                                        path - hostname), pool);
-          if (strncmp(hostname, "localhost", 9) == 0)
-            hostname = NULL;
-        }
-      else
-        hostname = NULL;
-    }
-
-  /* Duplicate the URL, starting at the top of the path.
-     At the same time, we URI-decode the path. */
-#if defined(WIN32) || defined(__CYGWIN__)
-  /* On Windows, we'll typically have to skip the leading / if the
-     path starts with a drive letter.  Like most Web browsers, We
-     support two variants of this scheme:
-
-         file:///X:/path    and
-         file:///X|/path
-
-    Note that, at least on WinNT and above,  file:////./X:/path  will
-    also work, so we must make sure the transformation doesn't break
-    that, and  file:///path  (that looks within the current drive
-    only) should also keep working.
-    If we got a non-empty hostname other than localhost, we convert this
-    into an UNC path.  In this case, we obviously don't strip the slash
-    even if the path looks like it starts with a drive letter.
-    Another thing to remember is that the form file:///\machine/share
-    was the only way to access UNC paths in svn before 1.2.  We
-    need to support that for compatibility with old working copies.
-  */
-  {
-    static const char valid_drive_letters[] =
-      "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
-    /* Casting away const! */
-    char *dup_path = (char *)svn_path_uri_decode(path, pool);
-    if (!hostname && dup_path[1] && strchr(valid_drive_letters, dup_path[1])
-        && (dup_path[2] == ':' || dup_path[2] == '|')
-        && (dup_path[3] == '/' || dup_path[3] == '\0'))
-      {
-        /* Skip the leading slash. */
-        ++dup_path;
-        /* We're using path below to calculate fs_path, so keep it in sync. */
-        ++path;
-        if (dup_path[1] == '|')
-          dup_path[1] = ':';
-
-        if (dup_path[3] == '\0')
-          {
-            /* A valid dirent for the driveroot must be like "C:/" instead of
-               just "C:" or svn_dirent_join() will use the current directory
-               on the drive instead */
-
-            char *new_path = apr_pcalloc(pool, 4);
-            new_path[0] = dup_path[0];
-            new_path[1] = ':';
-            new_path[2] = '/';
-            new_path[3] = '\0';
-          }
-      }
-    if (hostname)
-      /* We still know that the path starts with a slash. */
-      repos_root = apr_pstrcat(pool, "//", hostname, path, NULL);
-    else
-      repos_root = dup_path;
-  }
-#else
-  /* Currently, the only hostnames we are allowing on non-Win32 platforms
-     are the empty string and 'localhost'. */
-  if (hostname)
-    return svn_error_createf
-      (SVN_ERR_RA_ILLEGAL_URL, NULL,
-       _("Local URL '%s' contains unsupported hostname"), URL);
+    return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL,
+                             _("Local URL '%s' does not contain 'file://' "
+                               "prefix"), URL);
 
-  repos_root = svn_path_uri_decode(path, pool);
-#endif
+  SVN_ERR(svn_uri_get_dirent_from_file_url(&repos_dirent, URL, pool));
 
   /* Search for a repository in the full path. */
-  repos_root = svn_repos_find_root_path(repos_root, pool);
-  if (!repos_root)
-    return svn_error_createf
-      (SVN_ERR_RA_LOCAL_REPOS_OPEN_FAILED, NULL,
-       _("Unable to open repository '%s'"), URL);
+  repos_root_dirent = svn_repos_find_root_path(repos_dirent, pool);
+  if (!repos_root_dirent)
+    return svn_error_createf(SVN_ERR_RA_LOCAL_REPOS_OPEN_FAILED, NULL,
+                             _("Unable to open repository '%s'"), URL);
 
   /* Attempt to open a repository at URL. */
-  err = svn_repos_open(repos, repos_root, pool);
+  err = svn_repos_open(repos, repos_root_dirent, pool);
   if (err)
-    return svn_error_createf
-      (SVN_ERR_RA_LOCAL_REPOS_OPEN_FAILED, err,
-       _("Unable to open repository '%s'"), URL);
+    return svn_error_createf(SVN_ERR_RA_LOCAL_REPOS_OPEN_FAILED, err,
+                             _("Unable to open repository '%s'"), URL);
 
   /* Assert capabilities directly, since client == server. */
   {
@@ -166,26 +68,17 @@ svn_ra_local__split_URL(svn_repos_t **re
     SVN_ERR(svn_repos_remember_client_capabilities(*repos, caps));
   }
 
-  /* What remains of URL after being hacked at in the previous step is
-     REPOS_URL.  FS_PATH is what we've hacked off in the process.
-     Note that path is not encoded and what we gave to svn_root_find_root_path
-     may have been destroyed by that function.  So we have to decode it once
-     more.  But then, it is ours...
-     We want the suffix of path after the repos root part.  Note that
-     repos_root may contain //hostname, but path doesn't.  */
-  *fs_path = svn_path_uri_decode(path, pool)
-    + (strlen(repos_root)
-       - (hostname ? strlen(hostname) + 2 : 0));
-
-  /* Ensure that *FS_PATH has its leading slash. */
-  if (**fs_path != '/')
-    *fs_path = apr_pstrcat(pool, "/", *fs_path, NULL);
+  *fs_path = &repos_dirent[strlen(repos_root_dirent)];
+
+  if (**fs_path == '\0')
+    *fs_path = "/";
 
   /* Remove the path components in *fs_path from the original URL, to get
      the URL to the repository root. */
   urlbuf = svn_stringbuf_create(URL, pool);
   svn_path_remove_components(urlbuf,
-                             svn_path_component_count(*fs_path));
+                             svn_path_component_count(repos_dirent)
+                             - svn_path_component_count(repos_root_dirent));
   *repos_url = urlbuf->data;
 
   return SVN_NO_ERROR;

Modified: subversion/trunk/subversion/libsvn_subr/dirent_uri.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_subr/dirent_uri.c?rev=957893&r1=957892&r2=957893&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_subr/dirent_uri.c (original)
+++ subversion/trunk/subversion/libsvn_subr/dirent_uri.c Fri Jun 25 11:31:41 2010
@@ -2265,3 +2265,122 @@ svn_dirent_is_under_root(char **full_pat
 
   return status == APR_SUCCESS ? TRUE : FALSE;
 }
+
+svn_error_t *
+svn_uri_get_dirent_from_file_url(const char **dirent,
+                                 const char *url,
+                                 apr_pool_t *pool)
+{
+  const char *hostname, *path;
+
+  assert(svn_uri_is_canonical(url, pool));
+  assert(svn_path_is_url(url));
+
+  /* Verify that the URL is well-formed (loosely) */
+
+  /* First, check for the "file://" prefix. */
+  if (strncmp(url, "file://", 7) != 0)
+    return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL,
+                             _("Local URL '%s' does not contain 'file://' "
+                               "prefix"), url);
+
+  /* Then, skip what's between the "file://" prefix and the next
+     occurance of '/' -- this is the hostname, and we are considering
+     everything from that '/' until the end of the URL to be the
+     absolute path portion of the URL.
+     If we got just "file://", treat it the same as "file:///". */
+  hostname = url + 7;
+  if (*hostname == '\0')
+    {
+      path = "/";
+      hostname = NULL;
+    }
+  else
+    {
+      path = strchr(hostname, '/');
+      if (! path)
+        return
+           svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL,
+                             _("Local URL '%s' contains only a hostname, "
+                               "no path"), url);
+
+      /* Treat localhost as an empty hostname. */
+      if (hostname != path)
+        {
+          hostname = svn_path_uri_decode(apr_pstrmemdup(pool, hostname,
+                                                        path - hostname), pool);
+          if (strncmp(hostname, "localhost", 9) == 0)
+            hostname = NULL;
+        }
+      else
+        hostname = NULL;
+    }
+
+  /* Duplicate the URL, starting at the top of the path.
+     At the same time, we URI-decode the path. */
+#if defined(WIN32) || defined(__CYGWIN__)
+  /* On Windows, we'll typically have to skip the leading / if the
+     path starts with a drive letter.  Like most Web browsers, We
+     support two variants of this scheme:
+
+         file:///X:/path    and
+         file:///X|/path
+
+    Note that, at least on WinNT and above,  file:////./X:/path  will
+    also work, so we must make sure the transformation doesn't break
+    that, and  file:///path  (that looks within the current drive
+    only) should also keep working.
+    If we got a non-empty hostname other than localhost, we convert this
+    into an UNC path.  In this case, we obviously don't strip the slash
+    even if the path looks like it starts with a drive letter.
+  */
+  {
+    static const char valid_drive_letters[] =
+      "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
+    /* Casting away const! */
+    char *dup_path = (char *)svn_path_uri_decode(path, pool);
+
+    /* This check assumes ':' and '|' are already decoded! */
+    if (!hostname && dup_path[1] && strchr(valid_drive_letters, dup_path[1])
+        && (dup_path[2] == ':' || dup_path[2] == '|'))
+      {
+        /* Skip the leading slash. */
+        ++dup_path;
+
+        if (dup_path[1] == '|')
+          dup_path[1] = ':';
+
+        if (dup_path[2] == '/' || dup_path[2] == '\0')
+          {
+            if (dup_path[2] == '\0')
+              {
+                /* A valid dirent for the driveroot must be like "C:/" instead of
+                   just "C:" or svn_dirent_join() will use the current directory
+                   on the drive instead */
+                char *new_path = apr_pcalloc(pool, 4);
+                new_path[0] = dup_path[0];
+                new_path[1] = ':';
+                new_path[2] = '/';
+                new_path[3] = '\0';
+                dup_path = new_path;
+              }
+          }
+      }
+    if (hostname)
+      /* We still know that the path starts with a slash. */
+      *dirent = apr_pstrcat(pool, "//", hostname, dup_path, NULL);
+    else
+      *dirent = dup_path;
+  }
+#else
+  /* Currently, the only hostnames we are allowing on non-Win32 platforms
+     are the empty string and 'localhost'. */
+  if (hostname)
+    return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL,
+                             _("Local URL '%s' contains unsupported hostname"),
+                             URL);
+
+  repos_root = svn_path_uri_decode(path, pool);
+#endif
+  return SVN_NO_ERROR;
+}

Modified: subversion/trunk/subversion/tests/libsvn_subr/dirent_uri-test.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/tests/libsvn_subr/dirent_uri-test.c?rev=957893&r1=957892&r2=957893&view=diff
==============================================================================
--- subversion/trunk/subversion/tests/libsvn_subr/dirent_uri-test.c (original)
+++ subversion/trunk/subversion/tests/libsvn_subr/dirent_uri-test.c Fri Jun 25 11:31:41 2010
@@ -2670,6 +2670,45 @@ test_relpath_internal_style(apr_pool_t *
   return SVN_NO_ERROR;
 }
 
+static svn_error_t *
+test_dirent_from_file_url(apr_pool_t *pool)
+{
+  struct {
+    const char *url;
+    const char *result;
+  } tests[] = {
+    { "file:///dir",               "/dir" },
+    { "file:///dir/path",          "/dir/path" },
+    { "file://localhost/dir",      "/dir" },
+    { "file://localhost/dir/path", "/dir/path" },
+#if defined(WIN32)
+    { "file://server/share",       "//server/share" },
+    { "file://server/share/dir",   "//server/share/dir" },
+    { "file:///A:",                "A:/" },
+    { "file:///A:/dir",            "A:/dir" },
+    { "file:///A:dir",             "A:dir" },
+    { "file:///A%7C",              "A:/" },
+    { "file:///A%7C/dir",          "A:/dir" },
+    { "file:///A%7Cdir",           "A:dir" },
+#endif
+  };
+  int i;
+
+  for (i = 0; i < COUNT_OF(tests); i++)
+    {
+      const char *result;
+      
+      SVN_ERR(svn_uri_get_dirent_from_file_url(&result, tests[i].url, pool));
+
+      if (strcmp(result, tests[i].result))
+        return svn_error_createf(SVN_ERR_TEST_FAILED, NULL,
+                                 "svn_relpath_internal_style(\"%s\") returned "
+                                 "\"%s\" expected \"%s\"",
+                                 tests[i].url, result, tests[i].result);
+    }
+
+  return SVN_NO_ERROR;
+}
 
 /* The test table.  */
 
@@ -2762,5 +2801,7 @@ struct svn_test_descriptor_t test_funcs[
                    "test svn_dirent_internal_style"),
     SVN_TEST_PASS2(test_relpath_internal_style,
                    "test svn_relpath_internal_style"),
+    SVN_TEST_PASS2(test_dirent_from_file_url,
+                   "test svn_uri_get_dirent_from_file_url"),
     SVN_TEST_NULL
   };