git/list[1] front-page[2] threads[3] people[4] search[5] about
 

Re: [PATCH svnrdump-standalone] Sync with upstream

From
Jonathan Nieder <jrnieder@gmail.com>
Date
Aug 6, 2010, 18:37 UTC
Message-ID
<20100806183736.GA2985@burratino>
In-Reply-To
<20100806175709.GA2683@burratino>
Jonathan Nieder wrote:
> Delete all files and copy in newer versions from Subversion
> upstream (http://svn.apache.org/repos/asf/subversion/trunk@982862).
Here’s the “while at it” part:
Show 20 quoted lines
> While at it, port for an out-of-tree build against svn 1.6:
> 
>  - include a stripped-down version of the autoconf-generated
>    Makefile for Linux and a .gitignore file listing build
>    products;
> 
>  - use apr_hash_clear directly instead of svn_hash__clear
>    (since the latter’s signature has changed);
> 
>  - build and use an svn17_compat module for functions used
>    that were added in Subversion 1.7.
> 
> In theory, the svn17_compat module could shrink over time
> as simpler compatibility shims are devised.
>
> The result builds and all dump tests pass.
>
> Caveats: Most load tests do not pass.  Chances are this will only
> build against libsvn 1.6, not 1.7, since I did not do the appropriate
> identifier-renaming dance.
Most of this patch is Subversion code.

The rest, which is my own doing, I place in the public domain. You may freely use, modify, distribute, and relicense it.

 .gitignore      |    5 +
 Makefile        |   35 +++
 dump_editor.c   |    9 +-
 svn17_compat.c  |  842 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 svn17_compat.h  |  179 ++++++++++++
 svnrdump.c      |    5 +-
 svntest/main.py |   17 +-
 7 files changed, 1076 insertions(+), 16 deletions(-)
 create mode 100644 .gitignore
 create mode 100644 Makefile
 create mode 100644 svn17_compat.c
 create mode 100644 svn17_compat.h
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..af251b1
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,5 @@
+*.lo
+*.o
+*.pyc
+/svn-test-work/
+/svnrdump
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..b022411
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,35 @@
+CC=gcc
+PYTHON=python
+LIBTOOL=libtool
+LTFLAGS=--tag=CC --silent
+CFLAGS=-g -O2 -pthread -Wall -Werror=implicit-function-declaration
+EXEEXT=
+CPPFLAGS=-DLINUX=2 -D_REENTRANT -D_GNU_SOURCE -D_LARGEFILE64_SOURCE
+LDFLAGS=
+COMPILE=$(CC) $(CPPFLAGS) $(CFLAGS) $(INCLUDES)
+LINK=$(LIBTOOL) $(LTFLAGS) --mode=link $(CC) $(CFLAGS)
+LT_COMPILE=$(LIBTOOL) $(LTFLAGS) --mode=compile $(COMPILE) $(CFLAGS)
+
+INCLUDES=-I/usr/include/subversion-1 -I/usr/include/apr-1.0
+LIBS=-lsvn_client-1 -lsvn_ra-1 -lsvn_repos-1 -lsvn_delta-1 -lsvn_subr-1 -lapr-1
+OBJECTS=dump_editor.lo load_editor.lo svnrdump.lo svn17_compat.lo
+
+.SUFFIXES: .c .lo
+
+svnrdump$(EXEEXT): $(OBJECTS)
+	$(LINK) $(LDFLAGS) -o svnrdump$(EXEEXT) $(OBJECTS) $(LIBS)
+
+.c.lo:
+	$(LT_COMPILE) -o $@ -c $<
+
+dump_editor.lo: dump_editor.c dump_editor.h svn17_compat.h
+load_editor.lo: load_editor.c load_editor.h
+svnrdump.lo: svnrdump.c dump_editor.h load_editor.h svn17_compat.h
+svn17_compat.lo: svn17_compat.c svn17_compat.h
+
+check: svnrdump$(EXEEXT) svnrdump_tests.py
+	$(PYTHON) svnrdump_tests.py
+
+clean:
+	$(RM) svnrdump$(EXEEXT)
+	$(RM) *.lo *.o
diff --git a/dump_editor.c b/dump_editor.c
index dac9d15..72ad04f 100644
--- a/dump_editor.c
+++ b/dump_editor.c
@@ -29,6 +29,7 @@
 #include "svn_props.h"
 #include "svn_dirent_uri.h"
 
+#include "svn17_compat.h"
 #include "dump_editor.h"
 
 #define ARE_VALID_COPY_ARGS(p,r) ((p) && SVN_IS_VALID_REVNUM(r))
@@ -166,8 +167,8 @@ dump_props(struct dump_edit_baton *eb,
       SVN_ERR(svn_stream_printf(eb->stream, pool, "\n\n"));
 
       /* Cleanup so that data is never dumped twice. */
-      svn_hash__clear(eb->props, pool);
-      svn_hash__clear(eb->deleted_props, pool);
+      apr_hash_clear(eb->props);
+      apr_hash_clear(eb->deleted_props);
       if (trigger_var)
         *trigger_var = FALSE;
     }
@@ -711,8 +712,8 @@ close_file(void *file_baton,
 
       /* Cleanup */
       eb->dump_props = eb->dump_props_pending = FALSE;
-      svn_hash__clear(eb->props, pool);
-      svn_hash__clear(eb->deleted_props, pool);
+      apr_hash_clear(eb->props);
+      apr_hash_clear(eb->deleted_props);
     }
 
   /* Dump the text */
diff --git a/svn17_compat.c b/svn17_compat.c
new file mode 100644
index 0000000..e6d8774
--- /dev/null
+++ b/svn17_compat.c
@@ -0,0 +1,842 @@
+/*
+ * svn17_compat.c :   a library to make Subversion 1.6 look like 1.7.
+ *
+ * ====================================================================
+ *    This file is derived from code licensed to the Apache
+ *    Software Foundation (ASF) under one or more contributor
+ *    license agreements.  See the NOTICE file distributed with
+ *    this file for additional information regarding copyright
+ *    ownership.  The ASF licenses those portions to you under
+ *    the Apache License, Version 2.0 (the "License"); you may
+ *    not use those portions except in compliance with the
+ *    License.  You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing,
+ *    software distributed under the License is distributed on an
+ *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *    KIND, either express or implied.  See the License for the
+ *    specific language governing permissions and limitations
+ *    under the License.
+ *
+ *    Any code in this file not licensed from the ASF is
+ *    original code in the public domain.  You may freely use,
+ *    modify, distribute, and relicense such code.
+ * ====================================================================
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include <apr.h>
+#include <apr_strings.h>
+#include <apr_hash.h>
+#include <apr_lib.h>
+#include <apr_errno.h>
+#include <apr_pools.h>
+
+#include <svn_error.h>
+#include <svn_config.h>
+#include <svn_io.h>
+#include <svn_dirent_uri.h>
+#include <svn_path.h>
+#include "svn17_compat.h"
+
+/* TRUE if s is the canonical empty path, FALSE otherwise
+   From libsvn_subr/dirent_uri.c. */
+#define SVN_PATH_IS_EMPTY(s) ((s)[0] == '\0')
+
+/* Path type definition. Used only by internal functions.
+   From libsvn_subr/dirent_uri.c. */
+typedef enum {
+  type_uri,
+  type_dirent,
+  type_relpath
+} path_type_t;
+
+/* Here is the BNF for path components in a URI. "pchar" is a
+   character in a path component.
+
+      pchar       = unreserved | escaped |
+                    ":" | "@" | "&" | "=" | "+" | "$" | ","
+      unreserved  = alphanum | mark
+      mark        = "-" | "_" | "." | "!" | "~" | "*" | "'" | "(" | ")"
+
+   Note that "escaped" doesn't really apply to what users can put in
+   their paths, so that really means the set of characters is:
+
+      alphanum | mark | ":" | "@" | "&" | "=" | "+" | "$" | ","
+
+   From libsvn_subr/path.c. */
+static const char svn_uri__char_validity[256] = {
+  0, 0, 0, 0, 0, 0, 0, 0,   0, 0, 0, 0, 0, 0, 0, 0,
+  0, 0, 0, 0, 0, 0, 0, 0,   0, 0, 0, 0, 0, 0, 0, 0,
+  0, 1, 0, 0, 1, 0, 1, 1,   1, 1, 1, 1, 1, 1, 1, 1,
+  1, 1, 1, 1, 1, 1, 1, 1,   1, 1, 1, 0, 0, 1, 0, 0,
+
+  /* 64 */
+  1, 1, 1, 1, 1, 1, 1, 1,   1, 1, 1, 1, 1, 1, 1, 1,
+  1, 1, 1, 1, 1, 1, 1, 1,   1, 1, 1, 0, 0, 0, 0, 1,
+  0, 1, 1, 1, 1, 1, 1, 1,   1, 1, 1, 1, 1, 1, 1, 1,
+  1, 1, 1, 1, 1, 1, 1, 1,   1, 1, 1, 0, 0, 0, 1, 0,
+
+  /* 128 */
+  0, 0, 0, 0, 0, 0, 0, 0,   0, 0, 0, 0, 0, 0, 0, 0,
+  0, 0, 0, 0, 0, 0, 0, 0,   0, 0, 0, 0, 0, 0, 0, 0,
+  0, 0, 0, 0, 0, 0, 0, 0,   0, 0, 0, 0, 0, 0, 0, 0,
+  0, 0, 0, 0, 0, 0, 0, 0,   0, 0, 0, 0, 0, 0, 0, 0,
+
+  /* 192 */
+  0, 0, 0, 0, 0, 0, 0, 0,   0, 0, 0, 0, 0, 0, 0, 0,
+  0, 0, 0, 0, 0, 0, 0, 0,   0, 0, 0, 0, 0, 0, 0, 0,
+  0, 0, 0, 0, 0, 0, 0, 0,   0, 0, 0, 0, 0, 0, 0, 0,
+  0, 0, 0, 0, 0, 0, 0, 0,   0, 0, 0, 0, 0, 0, 0, 0,
+};
+
+/* From libsvn_subr/dirent_uri.c. */
+static svn_boolean_t
+svn_uri_is_canonical(const char *uri, apr_pool_t *pool)
+{
+  const char *ptr = uri, *seg = uri;
+  const char *schema_data = NULL;
+
+  /* URI is canonical if it has:
+   *  - no '.' segments
+   *  - no closing '/', unless for the root path '/' itself
+   *  - no '//'
+   *  - lowercase URL scheme
+   *  - lowercase URL hostname
+   */
+
+  if (*uri == '\0')
+    return TRUE;
+
+  /* Maybe parse hostname and scheme. */
+  if (*ptr != '/')
+    {
+      while (*ptr && (*ptr != '/') && (*ptr != ':'))
+        ptr++;
+
+      if (*ptr == ':' && *(ptr+1) == '/' && *(ptr+2) == '/')
+        {
+          /* Found a scheme, check that it's all lowercase. */
+          ptr = uri;
+          while (*ptr != ':')
+            {
+              if (*ptr >= 'A' && *ptr <= 'Z')
+                return FALSE;
+              ptr++;
+            }
+          /* Skip :// */
+          ptr += 3;
+
+          /* This might be the hostname */
+          seg = ptr;
+          while (*ptr && (*ptr != '/') && (*ptr != '@'))
+            ptr++;
+
+          if (! *ptr)
+            return TRUE;
+
+          if (*ptr == '@')
+            seg = ptr + 1;
+
+          /* Found a hostname, check that it's all lowercase. */
+          ptr = seg;
+          while (*ptr && *ptr != '/')
+            {
+              if (*ptr >= 'A' && *ptr <= 'Z')
+                return FALSE;
+              ptr++;
+            }
+
+          schema_data = ptr;
+        }
+      else
+        {
+          /* Didn't find a scheme; finish the segment. */
+          while (*ptr && *ptr != '/')
+            ptr++;
+        }
+    }
+
+#ifdef SVN_USE_DOS_PATHS
+  if (schema_data && *ptr == '/')
+    {
+      /* If this is a file url, ptr now points to the third '/' in
+         file:///C:/path. Check that if we have such a URL the drive
+         letter is in uppercase. */
+      if (strncmp(uri, "file:", 5) == 0 &&
+          ! (*(ptr+1) >= 'A' && *(ptr+1) <= 'Z') &&
+          *(ptr+2) == ':')
+        return FALSE;
+    }
+#endif /* SVN_USE_DOS_PATHS */
+
+  /* Now validate the rest of the URI. */
+  while(1)
+    {
+      apr_size_t seglen = ptr - seg;
+
+      if (seglen == 1 && *seg == '.')
+        return FALSE;  /*  /./   */
+
+      if (*ptr == '/' && *(ptr+1) == '/')
+        return FALSE;  /*  //    */
+
+      if (! *ptr && *(ptr - 1) == '/' && ptr - 1 != uri)
+        return FALSE;  /* foo/  */
+
+      if (! *ptr)
+        break;
+
+      if (*ptr == '/')
+        ptr++;
+      seg = ptr;
+
+
+      while (*ptr && (*ptr != '/'))
+        ptr++;
+    }
+
+  if (schema_data)
+    {
+      ptr = schema_data;
+
+      while (*ptr)
+        {
+          if (*ptr == '%')
+            {
+              char digitz[3];
+              int val;
+
+              /* Can't use apr_isxdigit() because lower case letters are
+                 not in our canonical format */
+              if (((*(ptr+1) < '0' || (*ptr+1) > '9'))
+                  && (*(ptr+1) < 'A' || (*ptr+1) > 'F'))
+                return FALSE;
+              else if (((*(ptr+2) < '0' || (*ptr+2) > '9'))
+                  && (*(ptr+2) < 'A' || (*ptr+2) > 'F'))
+                return FALSE;
+
+              digitz[0] = *(++ptr);
+              digitz[1] = *(++ptr);
+              digitz[2] = '\0';
+              val = (int)strtol(digitz, NULL, 16);
+
+              if (svn_uri__char_validity[val])
+                return FALSE; /* Should not have been escaped */
+            }
+          else if (*ptr != '/' && !svn_uri__char_validity[(unsigned char)*ptr])
+            return FALSE; /* Character should have been escaped */
+          ptr++;
+        }
+    }
+
+  return TRUE;
+}
+
+/* From libsvn_subr/dirent_uri.c. */
+static svn_boolean_t
+svn_uri_is_absolute(const char *uri)
+{
+  /* uri is absolute if it starts with '/' */
+  if (uri && uri[0] == '/')
+    return TRUE;
+
+  /* URLs are absolute. */
+  return svn_path_is_url(uri);
+}
+
+/* Calculates the length occupied by the schema defined root of URI
+   From libsvn_subr/dirent_uri.c. */
+static apr_size_t
+uri_schema_root_length(const char *uri, apr_size_t len)
+{
+  apr_size_t i;
+
+  for (i = 0; i < len; i++)
+    {
+      if (uri[i] == '/')
+        {
+          if (i > 0 && uri[i-1] == ':' && i < len-1 && uri[i+1] == '/')
+            {
+              /* We have an absolute uri */
+              if (i == 5 && strncmp("file", uri, 4) == 0)
+                return 7; /* file:// */
+              else
+                {
+                  for (i += 2; i < len; i++)
+                    if (uri[i] == '/')
+                      return i;
+
+                  return len; /* Only a hostname is found */
+                }
+            }
+          else
+            return 0;
+        }
+    }
+
+  return 0;
+}
+
+/* From libsvn_subr/dirent_uri.c. */
+char *
+svn_uri_join(const char *base, const char *component, apr_pool_t *pool)
+{
+  apr_size_t blen = strlen(base);
+  apr_size_t clen = strlen(component);
+  char *path;
+
+  assert(svn_uri_is_canonical(base, pool));
+  assert(svn_uri_is_canonical(component, pool));
+
+  /* If either is empty return the other */
+  if (SVN_PATH_IS_EMPTY(base))
+    return apr_pmemdup(pool, component, clen + 1);
+  if (SVN_PATH_IS_EMPTY(component))
+    return apr_pmemdup(pool, base, blen + 1);
+
+  /* If the component is absolute, then return it.  */
+  if (svn_uri_is_absolute(component))
+    {
+      if (*component != '/')
+        return apr_pmemdup(pool, component, clen + 1);
+      else
+        {
+          /* The uri is not absolute enough; use only the root from base */
+          apr_size_t n = uri_schema_root_length(base, blen);
+
+          path = apr_palloc(pool, n + clen + 1);
+
+          if (n > 0)
+            memcpy(path, base, n);
+
+          memcpy(path + n, component, clen + 1); /* Include '\0' */
+
+          return path;
+        }
+    }
+
+  if (blen == 1 && base[0] == '/')
+    blen = 0; /* Ignore base, just return separator + component */
+
+  /* Construct the new, combined path. */
+  path = apr_palloc(pool, blen + 1 + clen + 1);
+  memcpy(path, base, blen);
+  path[blen] = '/';
+  memcpy(path + blen + 1, component, clen + 1);
+
+  return path;
+}
+
+/* From libsvn_subr/dirent_uri.c. */
+static svn_boolean_t
+svn_relpath_is_canonical(const char *relpath,
+                         apr_pool_t *pool)
+{
+  const char *ptr = relpath, *seg = relpath;
+
+  /* RELPATH is canonical if it has:
+   *  - no '.' segments
+   *  - no start and closing '/'
+   *  - no '//'
+   */
+
+  if (*relpath == '\0')
+    return TRUE;
+
+  if (*ptr == '/')
+    return FALSE;
+
+  /* Now validate the rest of the path. */
+  while(1)
+    {
+      apr_size_t seglen = ptr - seg;
+
+      if (seglen == 1 && *seg == '.')
+        return FALSE;  /*  /./   */
+
+      if (*ptr == '/' && *(ptr+1) == '/')
+        return FALSE;  /*  //    */
+
+      if (! *ptr && *(ptr - 1) == '/')
+        return FALSE;  /* foo/  */
+
+      if (! *ptr)
+        break;
+
+      if (*ptr == '/')
+        ptr++;
+      seg = ptr;
+
+      while (*ptr && (*ptr != '/'))
+        ptr++;
+    }
+
+  return TRUE;
+}
+
+/* From libsvn_subr/dirent_uri.c. */
+const char *
+svn_relpath_basename(const char *relpath,
+                     apr_pool_t *pool)
+{
+  apr_size_t len = strlen(relpath);
+  apr_size_t start;
+
+  assert(!pool || svn_relpath_is_canonical(relpath, pool));
+
+  start = len;
+  while (start > 0 && relpath[start - 1] != '/')
+    --start;
+
+  if (pool)
+    return apr_pstrmemdup(pool, relpath + start, len - start);
+  else
+    return relpath + start;
+}
+
+/* From libsvn_subr/dirent_uri.c. */
+const char *
+svn_dirent_basename(const char *dirent, apr_pool_t *pool)
+{
+  apr_size_t len = strlen(dirent);
+  apr_size_t start;
+
+  assert(!pool || svn_dirent_is_canonical(dirent, pool));
+
+  if (svn_dirent_is_root(dirent, len))
+    return "";
+  else
+    {
+      start = len;
+      while (start > 0 && dirent[start - 1] != '/'
+#ifdef SVN_USE_DOS_PATHS
+             && dirent[start - 1] != ':'
+#endif
+            )
+        --start;
+    }
+
+  if (pool)
+    return apr_pstrmemdup(pool, dirent + start, len - start);
+  else
+    return dirent + start;
+}
+
+/* Locale insensitive tolower() for converting parts of dirents and urls
+   while canonicalizing.  From libsvn_subr/dirent_uri.c. */
+static char
+canonicalize_to_lower(char c)
+{
+  if (c < 'A' || c > 'Z')
+    return c;
+  else
+    return c - 'A' + 'a';
+}
+
+/* Locale insensitive toupper() for converting parts of dirents and urls
+   while canonicalizing.  From libsvn_subr/dirent_uri.c. */
+static char
+canonicalize_to_upper(char c)
+{
+  if (c < 'a' || c > 'z')
+    return c;
+  else
+    return c - 'a' + 'A';
+}
+
+
+/* Return the canonicalized version of PATH, of type TYPE, allocated in
+ * POOL.  From libsvn_subr/dirent_uri.c.
+ */
+static const char *
+canonicalize(path_type_t type, const char *path, apr_pool_t *pool)
+{
+  char *canon, *dst;
+  const char *src;
+  apr_size_t seglen;
+  apr_size_t schemelen = 0;
+  apr_size_t canon_segments = 0;
+  svn_boolean_t url = FALSE;
+  char *schema_data = NULL;
+
+  /* "" is already canonical, so just return it; note that later code
+     depends on path not being zero-length.  */
+  if (SVN_PATH_IS_EMPTY(path))
+    return "";
+
+  dst = canon = apr_pcalloc(pool, strlen(path) + 1);
+
+  /* If this is supposed to be an URI and it starts with "scheme://", then
+     copy the scheme, host name, etc. to DST and set URL = TRUE. */
+  src = path;
+  if (type == type_uri && *src != '/')
+    {
+      while (*src && (*src != '/') && (*src != ':'))
+        src++;
+
+      if (*src == ':' && *(src+1) == '/' && *(src+2) == '/')
+        {
+          const char *seg;
+
+          url = TRUE;
+
+          /* Found a scheme, convert to lowercase and copy to dst. */
+          src = path;
+          while (*src != ':')
+            {
+              *(dst++) = canonicalize_to_lower((*src++));
+              schemelen++;
+            }
+          *(dst++) = ':';
+          *(dst++) = '/';
+          *(dst++) = '/';
+          src += 3;
+          schemelen += 3;
+
+          /* This might be the hostname */
+          seg = src;
+          while (*src && (*src != '/') && (*src != '@'))
+            src++;
+
+          if (*src == '@')
+            {
+              /* Copy the username & password. */
+              seglen = src - seg + 1;
+              memcpy(dst, seg, seglen);
+              dst += seglen;
+              src++;
+            }
+          else
+            src = seg;
+
+          /* Found a hostname, convert to lowercase and copy to dst. */
+          while (*src && (*src != '/'))
+            *(dst++) = canonicalize_to_lower((*src++));
+
+          /* Copy trailing slash, or null-terminator. */
+          *(dst) = *(src);
+
+          /* Move src and dst forward only if we are not
+           * at null-terminator yet. */
+          if (*src)
+            {
+              src++;
+              dst++;
+              schema_data = dst;
+            }
+
+          canon_segments = 1;
+        }
+    }
+
+  /* Copy to DST any separator or drive letter that must come before the
+     first regular path segment. */
+  if (! url && type != type_relpath)
+    {
+      src = path;
+      /* If this is an absolute path, then just copy over the initial
+         separator character. */
+      if (*src == '/')
+        {
+          *(dst++) = *(src++);
+
+#ifdef SVN_USE_DOS_PATHS
+          /* On Windows permit two leading separator characters which means an
+           * UNC path. */
+          if ((type == type_dirent) && *src == '/')
+            *(dst++) = *(src++);
+#endif /* SVN_USE_DOS_PATHS */
+        }
+#ifdef SVN_USE_DOS_PATHS
+      /* On Windows the first segment can be a drive letter, which we normalize
+         to upper case. */
+      else if (type == type_dirent &&
+               ((*src >= 'a' && *src <= 'z') ||
+                (*src >= 'A' && *src <= 'Z')) &&
+               (src[1] == ':'))
+        {
+          *(dst++) = canonicalize_to_upper(*(src++));
+          /* Leave the ':' to be processed as (or as part of) a path segment
+             by the following code block, so we need not care whether it has
+             a slash after it. */
+        }
+#endif /* SVN_USE_DOS_PATHS */
+    }
+
+  while (*src)
+    {
+      /* Parse each segment, find the closing '/' */
+      const char *next = src;
+      while (*next && (*next != '/'))
+        ++next;
+
+      seglen = next - src;
+
+      if (seglen == 0 || (seglen == 1 && src[0] == '.'))
+        {
+          /* Noop segment, so do nothing. */
+        }
+#ifdef SVN_USE_DOS_PATHS
+      /* If this is the first path segment of a file:// URI and it contains a
+         windows drive letter, convert the drive letter to upper case. */
+      else if (url && canon_segments == 1 && seglen == 2 &&
+               (strncmp(canon, "file:", 5) == 0) &&
+               src[0] >= 'a' && src[0] <= 'z' && src[1] == ':')
+        {
+          *(dst++) = canonicalize_to_upper(src[0]);
+          *(dst++) = ':';
+          if (*next)
+            *(dst++) = *next;
+          canon_segments++;
+        }
+#endif /* SVN_USE_DOS_PATHS */
+      else
+        {
+          /* An actual segment, append it to the destination path */
+          if (*next)
+            seglen++;
+          memcpy(dst, src, seglen);
+          dst += seglen;
+          canon_segments++;
+        }
+
+      /* Skip over trailing slash to the next segment. */
+      src = next;
+      if (*src)
+        src++;
+    }
+
+  /* Remove the trailing slash if there was at least one
+   * canonical segment and the last segment ends with a slash.
+   *
+   * But keep in mind that, for URLs, the scheme counts as a
+   * canonical segment -- so if path is ONLY a scheme (such
+   * as "https://") we should NOT remove the trailing slash. */
+  if ((canon_segments > 0 && *(dst - 1) == '/')
+      && ! (url && path[schemelen] == '\0'))
+    {
+      dst --;
+    }
+
+  *dst = '\0';
+
+#ifdef SVN_USE_DOS_PATHS
+  /* Skip leading double slashes when there are less than 2
+   * canon segments. UNC paths *MUST* have two segments. */
+  if ((type == type_dirent) && canon[0] == '/' && canon[1] == '/')
+    {
+      if (canon_segments < 2)
+        return canon + 1;
+      else
+        {
+          /* Now we're sure this is a valid UNC path, convert the server name
+             (the first path segment) to lowercase as Windows treats it as case
+             insensitive.
+             Note: normally the share name is treated as case insensitive too,
+             but it seems to be possible to configure Samba to treat those as
+             case sensitive, so better leave that alone. */
+          dst = canon + 2;
+          while (*dst && *dst != '/')
+            *(dst++) = canonicalize_to_lower(*dst);
+        }
+    }
+#endif /* SVN_USE_DOS_PATHS */
+
+  /* Check the normalization of characters in a uri */
+  if (schema_data)
+    {
+      int need_extra = 0;
+      src = schema_data;
+
+      while (*src)
+        {
+          switch (*src)
+            {
+              case '/':
+                break;
+              case '%':
+                if (!apr_isxdigit(*(src+1)) || !apr_isxdigit(*(src+2)))
+                  need_extra += 2;
+                else
+                  src += 2;
+                break;
+              default:
+                if (!svn_uri__char_validity[(unsigned char)*src])
+                  need_extra += 2;
+                break;
+            }
+          src++;
+        }
+
+      if (need_extra > 0)
+        {
+          apr_size_t pre_schema_size = (apr_size_t)(schema_data - canon);
+
+          dst = apr_palloc(pool, (apr_size_t)(src - canon) + need_extra + 1);
+          memcpy(dst, canon, pre_schema_size);
+          canon = dst;
+
+          dst += pre_schema_size;
+        }
+      else
+        dst = schema_data;
+
+      src = schema_data;
+
+      while (*src)
+        {
+          switch (*src)
+            {
+              case '/':
+                *(dst++) = '/';
+                break;
+              case '%':
+                if (!apr_isxdigit(*(src+1)) || !apr_isxdigit(*(src+2)))
+                  {
+                    *(dst++) = '%';
+                    *(dst++) = '2';
+                    *(dst++) = '5';
+                  }
+                else
+                  {
+                    char digitz[3];
+                    int val;
+
+                    digitz[0] = *(++src);
+                    digitz[1] = *(++src);
+                    digitz[2] = 0;
+
+                    val = (int)strtol(digitz, NULL, 16);
+
+                    if (svn_uri__char_validity[(unsigned char)val])
+                      *(dst++) = (char)val;
+                    else
+                      {
+                        *(dst++) = '%';
+                        *(dst++) = canonicalize_to_upper(digitz[0]);
+                        *(dst++) = canonicalize_to_upper(digitz[1]);
+                      }
+                  }
+                break;
+              default:
+                if (!svn_uri__char_validity[(unsigned char)*src])
+                  {
+                    apr_snprintf(dst, 4, "%%%02X", (unsigned char)*src);
+                    dst += 3;
+                  }
+                else
+                  *(dst++) = *src;
+                break;
+            }
+          src++;
+        }
+      *dst = '\0';
+    }
+
+  return canon;
+}
+
+/* From libsvn_subr/dirent_uri.c. */
+const char *
+svn_uri_canonicalize(const char *uri, apr_pool_t *pool)
+{
+  return canonicalize(type_uri, uri, pool);
+}
+
+/* New code (public domain). */
+svn_error_t *
+svn_io_remove_file2(const char *path,
+                   svn_boolean_t ignore_enoent,
+                   apr_pool_t *scratch_pool)
+{
+  svn_error_t *err = svn_io_remove_file(path, scratch_pool);
+  if (ignore_enoent && err && APR_STATUS_IS_ENOENT(err->apr_err))
+    {
+      svn_error_clear(err);
+      return SVN_NO_ERROR;
+    }
+  return err;
+}
+
+/* From libsvn_subr/cmdline.c. */
+svn_error_t *
+svn_cmdline__apply_config_options(apr_hash_t *config,
+                                  const apr_array_header_t *config_options,
+                                  const char *prefix,
+                                  const char *argument_name)
+{
+  int i;
+
+  for (i = 0; i < config_options->nelts; i++)
+   {
+     svn_config_t *cfg;
+     svn_cmdline__config_argument_t *arg =
+                          APR_ARRAY_IDX(config_options, i,
+                                        svn_cmdline__config_argument_t *);
+
+     cfg = apr_hash_get(config, arg->file, APR_HASH_KEY_STRING);
+
+     if (cfg)
+       {
+         svn_config_set(cfg, arg->section, arg->option, arg->value);
+       }
+     else
+       {
+         svn_error_t *err = svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+             _("Unrecognized file in argument of %s"), argument_name);
+
+         svn_handle_warning2(stderr, err, prefix);
+         svn_error_clear(err);
+       }
+    }
+
+  return SVN_NO_ERROR;
+}
+
+/* From libsvn_subr/cmdline.c. */
+svn_error_t *
+svn_cmdline__parse_config_option(apr_array_header_t *config_options,
+                                 const char *opt_arg,
+                                 apr_pool_t *pool)
+{
+  svn_cmdline__config_argument_t *config_option;
+  const char *first_colon, *second_colon, *equals_sign;
+  apr_size_t len = strlen(opt_arg);
+  if ((first_colon = strchr(opt_arg, ':')) && (first_colon != opt_arg))
+    {
+      if ((second_colon = strchr(first_colon + 1, ':')) &&
+          (second_colon != first_colon + 1))
+        {
+          if ((equals_sign = strchr(second_colon + 1, '=')) &&
+              (equals_sign != second_colon + 1))
+            {
+              config_option = apr_pcalloc(pool, sizeof(*config_option));
+              config_option->file = apr_pstrndup(pool, opt_arg,
+                                                 first_colon - opt_arg);
+              config_option->section = apr_pstrndup(pool, first_colon + 1,
+                                                    second_colon - first_colon - 1);
+              config_option->option = apr_pstrndup(pool, second_colon + 1,
+                                                   equals_sign - second_colon -
+1);
+
+              if (! (strchr(config_option->option, ':')))
+                {
+                  config_option->value = apr_pstrndup(pool, equals_sign + 1,
+                                                      opt_arg + len - equals_sign - 1);
+                  APR_ARRAY_PUSH(config_options, svn_cmdline__config_argument_t
+*)
+                                       = config_option;
+                  return SVN_NO_ERROR;
+                }
+            }
+        }
+    }
+  return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+                          _("Invalid syntax of argument of --config-option"));
+}
diff --git a/svn17_compat.h b/svn17_compat.h
new file mode 100644
index 0000000..2c48cd6
--- /dev/null
+++ b/svn17_compat.h
@@ -0,0 +1,179 @@
+/**
+ * @copyright
+ * ====================================================================
+ *    This file is derived from code licensed to the Apache
+ *    Software Foundation (ASF) under one or more contributor
+ *    license agreements.  See the NOTICE file distributed with
+ *    this file for additional information regarding copyright
+ *    ownership.  The ASF licenses those portions to you under
+ *    the Apache License, Version 2.0 (the "License"); you may
+ *    not use those portions except in compliance with the
+ *    License.  You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing,
+ *    software distributed under the License is distributed on an
+ *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *    KIND, either express or implied.  See the License for the
+ *    specific language governing permissions and limitations
+ *    under the License.
+ *
+ *    Any code in this file not licensed from the ASF is
+ *    original code in the public domain.  You may freely use,
+ *    modify, distribute, and relicense such code.
+ * ====================================================================
+ * @endcopyright
+ *
+ * @file compat.h
+ * @brief SVN 1.7 compatibility routines.
+ */
+
+#ifndef SVN17_COMPAT_H
+#define SVN17_COMPAT_H
+
+#include <apr.h>
+#include <apr_pools.h>
+
+#include <svn_types.h>
+
+/** Join a valid base uri (@a base) with a relative path or uri
+ * (@a component), allocating the result in @a pool. @a component need
+ * not be a single component: it can be a relative path or a '/'
+ * prefixed relative path to join component to the root path of @a base.
+ *
+ * If @a component is the empty path, then @a base will be copied and
+ * returned.
+ *
+ * If the @a component is an absolute uri, then it is copied and returned.
+ *
+ * If @a component starts with a '/' and @a base contains a scheme, the
+ * scheme defined joining rules are applied.
+ *
+ * From svn_dirent_uri.h.
+ */
+char *
+svn_uri_join(const char *base,
+             const char *component,
+             apr_pool_t *pool);
+
+/** Get the basename of the specified canonicalized @a relpath.  The
+ * basename is defined as the last component of the relpath.  If the @a
+ * relpath has only one component then that is returned. The returned
+ * value will have no slashes in it.
+ *
+ * Example: svn_relpath_basename("/trunk/foo/bar") -> "bar"
+ *
+ * The returned basename will be allocated in @a pool. If @a
+ * pool is NULL a pointer to the basename in @a relpath is returned.
+ *
+ * @note If an empty string is passed, then an empty string will be returned.
+ *
+ * From svn_dirent_uri.h.
+ */
+const char *
+svn_relpath_basename(const char *uri,
+                     apr_pool_t *pool);
+
+/** Gets the name of the specified canonicalized @a dirent as it is known
+ * within its parent directory. If the @a dirent is root, return "". The
+ * returned value will not have slashes in it.
+ *
+ * Example: svn_dirent_basename("/foo/bar") -> "bar"
+ *
+ * The returned basename will be allocated in @a pool. If @a pool is NULL
+ * a pointer to the basename in @a dirent is returned.
+ *
+ * @note If an empty string is passed, then an empty string will be returned.
+ *
+ * From svn_dirent_uri.h.
+ */
+const char *
+svn_dirent_basename(const char *dirent,
+                    apr_pool_t *pool);
+
+/** Return a new uri like @a uri, but transformed such that some types
+ * of uri specification redundancies are removed.
+ *
+ * This involves collapsing redundant "/./" elements, removing
+ * multiple adjacent separator characters, removing trailing
+ * separator characters, and possibly other semantically inoperative
+ * transformations.
+ *
+ * If @a uri starts with a schema, this function also normalizes the
+ * escaping of the path component by unescaping characters that don't
+ * need escaping and escaping characters that do need escaping but
+ * weren't.
+ *
+ * This functions supports URLs.
+ *
+ * The returned uri may be statically allocated or allocated from @a pool.
+ *
+ * From svn_dirent_uri.h.
+ */
+const char *
+svn_uri_canonicalize(const char *uri,
+                     apr_pool_t *pool);
+
+/** Remove file @a path, a utf8-encoded path.  This wraps apr_file_remove(),
+ * converting any error to a Subversion error. If @a ignore_enoent is TRUE, and
+ * the file is not present (APR_STATUS_IS_ENOENT returns TRUE), then no
+ * error will be returned.
+ *
+ * From svn_io.h.
+ */
+svn_error_t *
+svn_io_remove_file2(const char *path,
+                   svn_boolean_t ignore_enoent,
+                   apr_pool_t *scratch_pool);
+
+/* From svn_private_config.h.
+ */
+#define PACKAGE_NAME "subversion"
+#define N_(x) x
+#include <locale.h>
+#include <libintl.h>
+#define _(x) dgettext(PACKAGE_NAME, x)
+
+/** Sets the config options in @a config_options, an apr array containing
+ * svn_cmdline__config_argument_t* elements to the configuration in @a cfg,
+ * a hash mapping of <tt>const char *</tt> configuration file names to
+ * @c svn_config_t *'s. Write warnings to stderr.
+ *
+ * Use @a prefix as prefix and @a argument_name in warning messages.
+ *
+ * From private/svn_cmdline_private.h.
+ */
+svn_error_t *
+svn_cmdline__apply_config_options(apr_hash_t *config,
+                                  const apr_array_header_t *config_options,
+                                  const char *prefix,
+                                  const char *argument_name);
+
+/** Container for config options parsed with svn_cmdline__parse_config_option
+ *
+ * From private/svn_cmdline_private.h.
+ */
+typedef struct svn_cmdline__config_argument_t
+{
+  const char *file;
+  const char *section;
+  const char *option;
+  const char *value;
+} svn_cmdline__config_argument_t;
+
+/** Parser for 'FILE:SECTION:OPTION=[VALUE]'-style option arguments.
+ *
+ * Parses @a opt_arg and places its value in @a config_options, an apr array
+ * containing svn_cmdline__config_argument_t* elements, allocating the option
+ * data in @a pool
+ *
+ * From private/svn_cmdline_private.h.
+ */
+svn_error_t *
+svn_cmdline__parse_config_option(apr_array_header_t *config_options,
+                                 const char *opt_arg,
+                                 apr_pool_t *pool);
+
+
+#endif /* SVN17_COMPAT_H */
diff --git a/svnrdump.c b/svnrdump.c
index daf826e..a320b24 100644
--- a/svnrdump.c
+++ b/svnrdump.c
@@ -30,15 +30,14 @@
 #include "svn_repos.h"
 #include "svn_path.h"
 #include "svn_utf.h"
-#include "svn_private_config.h"
 #include "svn_string.h"
 #include "svn_props.h"
+#include "svn_dirent_uri.h"
 
+#include "svn17_compat.h"
 #include "dump_editor.h"
 #include "load_editor.h"
 
-#include "private/svn_cmdline_private.h"
-
 static svn_opt_subcommand_t dump_cmd, load_cmd;
 
 enum svn_svnrdump__longopt_t
diff --git a/svntest/main.py b/svntest/main.py
index 2c59101..3e938d6 100644
--- a/svntest/main.py
+++ b/svntest/main.py
@@ -149,15 +149,14 @@ def url2pathname(path):
 # The locations of the svn, svnadmin and svnlook binaries, relative to
 # the only scripts that import this file right now (they live in ../).
 # Use --bin to override these defaults.
-svn_binary = os.path.abspath('../../svn/svn' + _exe)
-svnadmin_binary = os.path.abspath('../../svnadmin/svnadmin' + _exe)
-svnlook_binary = os.path.abspath('../../svnlook/svnlook' + _exe)
-svnrdump_binary = os.path.abspath('../../svnrdump/svnrdump' + _exe)
-svnsync_binary = os.path.abspath('../../svnsync/svnsync' + _exe)
-svnversion_binary = os.path.abspath('../../svnversion/svnversion' + _exe)
-svndumpfilter_binary = os.path.abspath('../../svndumpfilter/svndumpfilter' + \
-                                       _exe)
-entriesdump_binary = os.path.abspath('entries-dump' + _exe)
+svn_binary = 'svn'
+svnadmin_binary = 'svnadmin'
+svnlook_binary = 'svnlook'
+svnrdump_binary = os.path.abspath('./svnrdump' + _exe)
+svnsync_binary = 'svnsync'
+svnversion_binary = 'svnversion'
+svndumpfilter_binary = 'svndumpfilter'
+entriesdump_binary = 'entries-dump'
 
 # Location to the pristine repository, will be calculated from test_area_url
 # when we know what the user specified for --url.
-- 
1.7.2.1.544.ga752d.dirty
Previous: Ramkumar RamachandraNext: Jonathan Nieder
Message 23 of 26 in “Get svnrdump merged into git.git”
  1. 0/9 Get svnrdump merged into git.gitRamkumar Ramachandra, Jul 13, 2010
  2. 1/9 Add LICENSERamkumar Ramachandra, Jul 13, 2010
  3. Daniel ShahafJul 14, 2010
  4. Ramkumar RamachandraJul 14, 2010
  5. 2/9 Add skeleton SVN client and MakefileRamkumar Ramachandra, Jul 13, 2010
  6. 3/9 Add debug editor from Subversion trunkRamkumar Ramachandra, Jul 13, 2010
  7. 4/9 Drive the debug editorRamkumar Ramachandra, Jul 13, 2010
  8. 5/9 Dump the revprops at the start of every revisionRamkumar Ramachandra, Jul 13, 2010
  9. 6/9 Implement directory-related functionsRamkumar Ramachandra, Jul 13, 2010
  10. 7/9 Implement file-related functionsRamkumar Ramachandra, Jul 13, 2010
  11. 8/9 Implement close_fileRamkumar Ramachandra, Jul 13, 2010
  12. 9/9 Add a validation scriptRamkumar Ramachandra, Jul 13, 2010
  13. Ramkumar RamachandraJul 13, 2010
  14. Jonathan NiederJul 14, 2010
  15. Ramkumar RamachandraJul 14, 2010
  16. Jonathan NiederJul 14, 2010
  17. Ramkumar RamachandraJul 14, 2010
  18. Stefan SperlingJul 14, 2010
  19. Ramkumar RamachandraJul 14, 2010
  20. Stefan SperlingJul 14, 2010
  21. Junio C HamanoJul 14, 2010
  22. Ramkumar RamachandraJul 15, 2010
  23. Jonathan NiederAug 6, 2010
  24. Jonathan NiederAug 7, 2010
  25. Ramkumar RamachandraAug 7, 2010
  26. Ramkumar RamachandraAug 7, 2010

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.