You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by cm...@apache.org on 2012/11/27 23:13:38 UTC

svn commit: r1414433 [3/4] - in /subversion/branches/issue-4194-dev: ./ build/ac-macros/ subversion/include/ subversion/include/private/ subversion/libsvn_client/ subversion/libsvn_delta/ subversion/libsvn_diff/ subversion/libsvn_fs_fs/ subversion/libs...

Modified: subversion/branches/issue-4194-dev/subversion/libsvn_wc/wc_db.c
URL: http://svn.apache.org/viewvc/subversion/branches/issue-4194-dev/subversion/libsvn_wc/wc_db.c?rev=1414433&r1=1414432&r2=1414433&view=diff
==============================================================================
--- subversion/branches/issue-4194-dev/subversion/libsvn_wc/wc_db.c (original)
+++ subversion/branches/issue-4194-dev/subversion/libsvn_wc/wc_db.c Tue Nov 27 22:13:24 2012
@@ -32,6 +32,7 @@
 #include "svn_dirent_uri.h"
 #include "svn_path.h"
 #include "svn_hash.h"
+#include "svn_sorts.h"
 #include "svn_wc.h"
 #include "svn_checksum.h"
 #include "svn_pools.h"
@@ -381,7 +382,18 @@ wclock_owns_lock(svn_boolean_t *own_lock
                  svn_boolean_t exact,
                  apr_pool_t *scratch_pool);
 
+/* Baton for db_is_switched */
+struct db_is_switched_baton_t
+{
+  svn_boolean_t *is_switched;
+  svn_kind_t *kind;
+};
 
+static svn_error_t *
+db_is_switched(void *baton,
+               svn_wc__db_wcroot_t *wcroot,
+               const char *local_relpath,
+               apr_pool_t *scratch_pool);
  
 /* Return the absolute path, in local path style, of LOCAL_RELPATH
    in WCROOT.  */
@@ -1778,6 +1790,7 @@ svn_wc__db_base_add_file(svn_wc__db_t *d
                          svn_boolean_t delete_working,
                          svn_boolean_t update_actual_props,
                          apr_hash_t *new_actual_props,
+                         apr_array_header_t *new_iprops,
                          svn_boolean_t keep_recorded_info,
                          svn_boolean_t insert_base_deleted,
                          const svn_skel_t *conflict,
@@ -1821,6 +1834,7 @@ svn_wc__db_base_add_file(svn_wc__db_t *d
   ibb.checksum = checksum;
 
   ibb.dav_cache = dav_cache;
+  ibb.iprops = new_iprops;
 
   if (update_actual_props)
     {
@@ -1863,6 +1877,7 @@ svn_wc__db_base_add_symlink(svn_wc__db_t
                             svn_boolean_t delete_working,
                             svn_boolean_t update_actual_props,
                             apr_hash_t *new_actual_props,
+                            apr_array_header_t *new_iprops,
                             svn_boolean_t keep_recorded_info,
                             svn_boolean_t insert_base_deleted,
                             const svn_skel_t *conflict,
@@ -1905,6 +1920,7 @@ svn_wc__db_base_add_symlink(svn_wc__db_t
   ibb.target = target;
 
   ibb.dav_cache = dav_cache;
+  ibb.iprops = new_iprops;
 
   if (update_actual_props)
     {
@@ -7663,7 +7679,7 @@ read_info(svn_wc__db_status_t *status,
           if (op_depth != 0)
             *lock = NULL;
           else
-            *lock = lock_from_columns(stmt_info, 16, 17, 18, 19, result_pool);
+            *lock = lock_from_columns(stmt_info, 17, 18, 19, 20, result_pool);
         }
 
       if (have_work)
@@ -9014,6 +9030,45 @@ svn_wc__db_prop_retrieve_recursive(apr_h
   return svn_error_trace(svn_sqlite__reset(stmt));
 }
 
+/* Baton for db_read_cached_iprops */
+struct read_cached_iprops_baton_t
+{
+  apr_array_header_t *iprops;
+  apr_pool_t *result_pool;
+};
+
+/* Implements svn_wc__db_txn_callback_t for svn_wc__db_read_cached_iprops */
+static svn_error_t *
+db_read_cached_iprops(void *baton,
+                      svn_wc__db_wcroot_t *wcroot,
+                      const char *local_relpath,
+                      apr_pool_t *scratch_pool)
+{
+  struct read_cached_iprops_baton_t *rib = baton;
+  svn_sqlite__stmt_t *stmt;
+  svn_boolean_t have_row;
+
+  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_SELECT_IPROPS));
+  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
+  SVN_ERR(svn_sqlite__step(&have_row, stmt));
+
+  if (!have_row)
+    {
+      return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND,
+                               svn_sqlite__reset(stmt),
+                               _("The node '%s' was not found."),
+                               path_for_error_message(wcroot, local_relpath,
+                                                      scratch_pool));
+    }
+
+  SVN_ERR(svn_sqlite__column_iprops(&rib->iprops, stmt, 0,
+                                    rib->result_pool, scratch_pool));
+
+  SVN_ERR(svn_sqlite__reset(stmt));
+
+  return SVN_NO_ERROR;
+}
+
 svn_error_t *
 svn_wc__db_read_cached_iprops(apr_array_header_t **iprops,
                               svn_wc__db_t *db,
@@ -9023,91 +9078,269 @@ svn_wc__db_read_cached_iprops(apr_array_
 {
   svn_wc__db_wcroot_t *wcroot;
   const char *local_relpath;
-  svn_sqlite__stmt_t *stmt;
-  svn_boolean_t have_row;
-  const char *repos_root_url;
-  svn_revnum_t revision;
-  int op_depth;
-  const char *repos_relpath;
+  struct read_cached_iprops_baton_t rcib;
 
   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
 
-  SVN_ERR(svn_wc__db_read_info(NULL, NULL,
-                               &revision, &repos_relpath, &repos_root_url,
-                               NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-                               NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-                               NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-                               NULL, db, local_abspath, result_pool,
-                               scratch_pool));
+  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
+                                                db, local_abspath,
+                                                scratch_pool, scratch_pool));
+  VERIFY_USABLE_WCROOT(wcroot);
+
+  rcib.result_pool = result_pool;
 
-  if (repos_relpath && repos_relpath[0] == '\0')
+  /* Don't use with_txn yet, as we perform just a single transaction */
+  SVN_ERR(db_read_cached_iprops(&rcib, wcroot, local_relpath, scratch_pool));
+
+  if (rcib.iprops)
+    *iprops = rcib.iprops;
+  else
     {
-      /* LOCAL_ABSPATH reflects the root of the repository, so there is
-         no parents to inherit from. */
       *iprops = apr_array_make(result_pool, 0,
                                sizeof(svn_prop_inherited_item_t *));
     }
-  else
+
+  return SVN_NO_ERROR;
+}
+
+/* Remove all prop name value pairs from PROP_HASH where the property
+   name is not PROPNAME. */
+static void
+filter_unwanted_props(apr_hash_t *prop_hash,
+                      const char * propname,
+                      apr_pool_t *scratch_pool)
+{
+  apr_hash_index_t *hi;
+
+  for (hi = apr_hash_first(scratch_pool, prop_hash);
+       hi;
+       hi = apr_hash_next(hi))
     {
-      SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
-                                                    db, local_abspath,
-                                                    scratch_pool,
-                                                    scratch_pool));
-      VERIFY_USABLE_WCROOT(wcroot);
-      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
-                                        STMT_SELECT_IPROPS));
-      SVN_ERR(op_depth_of(&op_depth, wcroot, local_relpath));
-      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
-      SVN_ERR(svn_sqlite__step(&have_row, stmt));
+      const char *ipropname = svn__apr_hash_index_key(hi);
 
-      if (!have_row)
+      if (strcmp(ipropname, propname) != 0)
+        apr_hash_set(prop_hash, ipropname, APR_HASH_KEY_STRING, NULL);
+    }
+  return;
+}
+
+/* Baton for db_read_inherited_props */
+struct read_inherited_props_baton_t
+{
+  apr_array_header_t *iprops;
+  const char *propname;
+  apr_pool_t *result_pool;
+};
+
+/* Implements svn_wc__db_txn_callback_t for svn_wc__db_read_inherited_props */
+static svn_error_t *
+db_read_inherited_props(void *baton,
+                        svn_wc__db_wcroot_t *wcroot,
+                        const char *local_relpath,
+                        apr_pool_t *scratch_pool)
+{
+  struct read_inherited_props_baton_t *ripb = baton;
+  int i;
+  apr_array_header_t *cached_iprops = NULL;
+  const char *parent_relpath = local_relpath;
+  svn_boolean_t is_wc_root = FALSE;
+  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
+  apr_pool_t *result_pool = ripb->result_pool;
+
+  ripb->iprops = apr_array_make(ripb->result_pool, 1,
+                                sizeof(svn_prop_inherited_item_t *));
+
+  /* Walk up to the root of the WC looking for inherited properties.  When we
+     reach the WC root also check for cached inherited properties. */
+  while (TRUE)
+    {
+      apr_hash_t *actual_props;
+      svn_boolean_t is_switched;
+      struct db_is_switched_baton_t swb;
+
+      svn_pool_clear(iterpool);
+
+      swb.is_switched = &is_switched;
+      swb.kind = NULL;
+
+      if (*parent_relpath == '\0')
         {
-          /* No cached iprops. */
-          *iprops = NULL;
+          is_switched = FALSE;
+          is_wc_root = TRUE;
         }
       else
+        SVN_ERR(db_is_switched(&swb, wcroot, parent_relpath, scratch_pool));
+
+      if (is_switched || is_wc_root)
         {
-          SVN_ERR(svn_sqlite__column_iprops(iprops, stmt, 0, result_pool,
-                                            scratch_pool));
-         }
+          struct read_cached_iprops_baton_t rib;
+          is_wc_root = TRUE;
 
-      SVN_ERR(svn_sqlite__reset(stmt));
+          rib.result_pool = scratch_pool;
+
+          /* If the WC root is also the root of the repository then by
+             definition there are no inheritable properties to be had,
+             but checking for that is just as expensive as fetching them
+             anyway. */
+
+          /* Grab the cached inherited properties for the WC root. */
+          SVN_ERR(db_read_cached_iprops(&rib, wcroot, parent_relpath,
+                                       iterpool));
+
+          cached_iprops = rib.iprops;
+        }
+
+      /* If PARENT_ABSPATH is a true parent of LOCAL_ABSPATH, then
+         LOCAL_ABSPATH can inherit properties from it. */
+      if (strcmp(local_relpath, parent_relpath) != 0)
+        {
+          struct db_read_props_baton_t rpb;
+          rpb.result_pool = result_pool;
+
+          SVN_ERR(db_read_props(&rpb, wcroot, parent_relpath, iterpool));
+
+          actual_props = rpb.props;
+
+          if (actual_props)
+            {
+              /* If we only want PROPNAME filter out any other properties. */
+              if (ripb->propname)
+                filter_unwanted_props(actual_props, ripb->propname, iterpool);
+
+              if (apr_hash_count(actual_props))
+                {
+                  svn_prop_inherited_item_t *iprop_elt =
+                    apr_pcalloc(ripb->result_pool,
+                                sizeof(svn_prop_inherited_item_t));
+                  iprop_elt->path_or_url = svn_dirent_join(wcroot->abspath,
+                                                           parent_relpath,
+                                                           result_pool);
+
+                  iprop_elt->prop_hash = actual_props;
+                  /* Build the output array in depth-first order. */
+                  svn_sort__array_insert(&iprop_elt, ripb->iprops, 0);
+                }
+            }
+        }
+
+      /* Inheritance only goes as far as the nearest WC root. */
+      if (is_wc_root)
+        break;
+
+      /* Keep looking for the WC root. */
+      parent_relpath = svn_relpath_dirname(parent_relpath, scratch_pool);
+    }
+
+  if (cached_iprops)
+    {
+      for (i = cached_iprops->nelts - 1; i >= 0; i--)
+        {
+          svn_prop_inherited_item_t *cached_iprop =
+            APR_ARRAY_IDX(cached_iprops, i, svn_prop_inherited_item_t *);
+
+          /* An empty property hash in the iprops cache means there are no
+             inherited properties. */
+          if (apr_hash_count(cached_iprop->prop_hash) == 0)
+            continue;
+
+          if (ripb->propname)
+            filter_unwanted_props(cached_iprop->prop_hash, ripb->propname,
+                                  scratch_pool);
+
+          /* If we didn't filter everything then keep this iprop. */
+          if (apr_hash_count(cached_iprop->prop_hash))
+            svn_sort__array_insert(&cached_iprop, ripb->iprops, 0);
+        }
     }
 
+  svn_pool_destroy(iterpool);
   return SVN_NO_ERROR;
 }
 
-/* Recursive body of svn_wc__db_get_children_with_cached_iprops. */
-static svn_error_t *
-get_children_with_cached_iprops(apr_hash_t *iprop_paths,
-                                svn_depth_t depth,
-                                const char *local_abspath,
+svn_error_t *
+svn_wc__db_read_inherited_props(apr_array_header_t **iprops,
                                 svn_wc__db_t *db,
+                                const char *local_abspath,
+                                const char *propname,
                                 apr_pool_t *result_pool,
                                 apr_pool_t *scratch_pool)
 {
   svn_wc__db_wcroot_t *wcroot;
   const char *local_relpath;
-  svn_sqlite__stmt_t *stmt;
-  svn_boolean_t have_row;
+  struct read_inherited_props_baton_t ripb;
 
   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
 
-  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
-                                                local_abspath, scratch_pool,
-                                                scratch_pool));
+  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
+                                                db, local_abspath,
+                                                scratch_pool, scratch_pool));
   VERIFY_USABLE_WCROOT(wcroot);
-  if (depth == svn_depth_empty
-      || depth == svn_depth_files
-      || depth == svn_depth_immediates)
+
+  ripb.iprops = NULL;
+  ripb.propname = propname;
+  ripb.result_pool = result_pool;
+
+  SVN_ERR(svn_wc__db_with_txn(wcroot, local_relpath, db_read_inherited_props,
+                              &ripb, scratch_pool));
+
+  *iprops = ripb.iprops;
+  return SVN_NO_ERROR;
+}
+
+/* Baton for get_children_with_cached_iprops */
+struct get_children_with_cached_baton_t
+{
+  svn_depth_t depth;
+  apr_hash_t *iprop_paths;
+  apr_pool_t *result_pool;
+};
+
+/* Implements svn_wc__db_txn_callback_t for
+   svn_wc__db_get_children_with_cached_iprops. */
+static svn_error_t *
+get_children_with_cached_iprops(void *baton,
+                                svn_wc__db_wcroot_t *wcroot,
+                                const char *local_relpath,
+                                apr_pool_t *scratch_pool)
+{
+  struct get_children_with_cached_baton_t *cwcb = baton;
+  svn_sqlite__stmt_t *stmt;
+  svn_boolean_t have_row;
+  apr_hash_t *iprop_paths = cwcb->iprop_paths;
+  apr_pool_t *result_pool = cwcb->result_pool;
+
+  /* First check if LOCAL_RELPATH itself has iprops */
+  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
+                                    STMT_SELECT_IPROPS_NODE));
+  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
+  SVN_ERR(svn_sqlite__step(&have_row, stmt));
+
+  if (have_row)
+   {
+      const char *relpath_with_cache = svn_sqlite__column_text(stmt, 0,
+                                                               NULL);
+      const char *abspath_with_cache = svn_dirent_join(wcroot->abspath,
+                                                       relpath_with_cache,
+                                                       result_pool);
+      apr_hash_set(iprop_paths, abspath_with_cache, APR_HASH_KEY_STRING,
+                   svn_sqlite__column_text(stmt, 1, result_pool));
+    }
+  SVN_ERR(svn_sqlite__reset(stmt));
+
+  if (cwcb->depth == svn_depth_empty)
+    return SVN_NO_ERROR;
+
+  /* Now fetch information for children or all descendants */
+  if (cwcb->depth == svn_depth_files
+      || cwcb->depth == svn_depth_immediates)
     {
       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
-                                        STMT_SELECT_INODES));  
+                                        STMT_SELECT_IPROPS_CHILDREN));
     }
   else /* Default to svn_depth_infinity. */
     {
       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
-                                        STMT_SELECT_INODES_RECURSIVE));
+                                        STMT_SELECT_IPROPS_RECURSIVE));
     }
 
   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
@@ -9121,46 +9354,50 @@ get_children_with_cached_iprops(apr_hash
                                                        relpath_with_cache,
                                                        result_pool);
       apr_hash_set(iprop_paths, abspath_with_cache, APR_HASH_KEY_STRING,
-                   abspath_with_cache);
+                   svn_sqlite__column_text(stmt, 1, result_pool));
       SVN_ERR(svn_sqlite__step(&have_row, stmt));
     }
 
   SVN_ERR(svn_sqlite__reset(stmt));
 
-  if (depth == svn_depth_files || depth == svn_depth_immediates)
+  /* For depth files we should filter non files */
+  if (cwcb->depth == svn_depth_files)
     {
-      const apr_array_header_t *rel_children;
-      int i;
+      apr_hash_index_t *hi;
       apr_pool_t *iterpool = svn_pool_create(scratch_pool);
 
-      SVN_ERR(svn_wc__db_read_children_of_working_node(&rel_children,
-                                                       db, local_abspath,
-                                                       scratch_pool,
-                                                       scratch_pool));
-      for (i = 0; i < rel_children->nelts; i++)
+      for (hi = apr_hash_first(scratch_pool, iprop_paths);
+           hi;
+           hi = apr_hash_next(hi))
         {
-          const char *child_abspath;
+          const char *child_abspath = svn__apr_hash_index_key(hi);
+          const char *child_relpath;
+          svn_kind_t child_kind;
 
           svn_pool_clear(iterpool);
-          child_abspath = svn_dirent_join(
-            local_abspath, APR_ARRAY_IDX(rel_children, i, const char *),
-            iterpool);
 
-          if (depth == svn_depth_files)
-            {
-              svn_kind_t child_kind;
+          child_relpath = svn_dirent_is_child(local_relpath, child_abspath,
+                                              NULL);
 
-              SVN_ERR(svn_wc__db_read_kind(&child_kind, db, child_abspath,
-                                           FALSE, FALSE, iterpool));
-              if (child_kind != svn_kind_file)
-                continue;
+          if (! child_relpath)
+            {
+              continue; /* local_relpath itself */
             }
 
-          SVN_ERR(get_children_with_cached_iprops(iprop_paths,
-                                                  svn_depth_empty,
-                                                  child_abspath, db,
-                                                  result_pool,
-                                                  iterpool));
+          SVN_ERR(svn_wc__db_base_get_info_internal(NULL, &child_kind, NULL,
+                                                    NULL, NULL, NULL, NULL,
+                                                    NULL, NULL, NULL, NULL,
+                                                    NULL, NULL, NULL,
+                                                    wcroot, child_relpath,
+                                                    scratch_pool,
+                                                    scratch_pool));
+
+          /* Filter if not a file */
+          if (child_kind != svn_kind_file)
+            {
+              apr_hash_set(iprop_paths, child_abspath, APR_HASH_KEY_STRING,
+                           NULL);
+            }
         }
 
       svn_pool_destroy(iterpool);
@@ -9177,10 +9414,26 @@ svn_wc__db_get_children_with_cached_ipro
                                            apr_pool_t *result_pool,
                                            apr_pool_t *scratch_pool)
 {
-  *iprop_paths = apr_hash_make(result_pool);
-  SVN_ERR(get_children_with_cached_iprops(*iprop_paths, depth,
-                                          local_abspath, db, result_pool,
-                                          scratch_pool));
+  svn_wc__db_wcroot_t *wcroot;
+  const char *local_relpath;
+  struct get_children_with_cached_baton_t cwcb;
+
+  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
+
+  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
+                                                local_abspath, scratch_pool,
+                                                scratch_pool));
+  VERIFY_USABLE_WCROOT(wcroot);
+
+  cwcb.iprop_paths = apr_hash_make(result_pool);
+  cwcb.depth = depth;
+  cwcb.result_pool = result_pool;
+
+  SVN_ERR(svn_wc__db_with_txn(wcroot, local_relpath,
+                              get_children_with_cached_iprops, &cwcb,
+                              scratch_pool));
+
+  *iprop_paths = cwcb.iprop_paths;
   return SVN_NO_ERROR;
 }
 
@@ -9686,6 +9939,7 @@ commit_node(void *baton,
   svn_sqlite__stmt_t *stmt_act;
   svn_boolean_t have_act;
   svn_string_t prop_blob = { 0 };
+  svn_string_t inherited_prop_blob = { 0 };
   const char *changelist = NULL;
   const char *parent_relpath;
   svn_wc__db_status_t new_presence;
@@ -9755,6 +10009,10 @@ commit_node(void *baton,
     prop_blob.data = svn_sqlite__column_blob(stmt_info, 14, &prop_blob.len,
                                              scratch_pool);
 
+  inherited_prop_blob.data = svn_sqlite__column_blob(stmt_info, 16,
+                                                     &inherited_prop_blob.len,
+                                                     scratch_pool);
+
   if (cb->keep_changelist && have_act)
     changelist = svn_sqlite__column_text(stmt_act, 0, scratch_pool);
 
@@ -9841,6 +10099,11 @@ commit_node(void *baton,
                                     scratch_pool));
   SVN_ERR(svn_sqlite__bind_properties(stmt, 15, cb->new_dav_cache,
                                       scratch_pool));
+  if (inherited_prop_blob.data != NULL)
+    {
+      SVN_ERR(svn_sqlite__bind_blob(stmt, 17, inherited_prop_blob.data,
+                                    inherited_prop_blob.len));
+    }
 
   SVN_ERR(svn_sqlite__step_done(stmt));
 
@@ -12475,6 +12738,118 @@ svn_wc__db_is_wcroot(svn_boolean_t *is_r
    return SVN_NO_ERROR;
 }
 
+/* This implements svn_wc__db_txn_callback_t for svn_wc_db__is_switched */
+static svn_error_t *
+db_is_switched(void *baton,
+               svn_wc__db_wcroot_t *wcroot,
+               const char *local_relpath,
+               apr_pool_t *scratch_pool)
+{
+  struct db_is_switched_baton_t *isb = baton;
+  svn_wc__db_status_t status;
+  apr_int64_t repos_id;
+  const char *repos_relpath;
+  const char *name;
+  const char *parent_local_relpath;
+  apr_int64_t parent_repos_id;
+  const char *parent_repos_relpath;
+
+  SVN_ERR_ASSERT(*local_relpath != '\0'); /* Handled in wrapper */
+
+  SVN_ERR(read_info(&status, isb->kind, NULL, &repos_relpath, &repos_id, NULL,
+                    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+                    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+                    wcroot, local_relpath, scratch_pool, scratch_pool));
+
+  if (status == svn_wc__db_status_server_excluded
+      || status == svn_wc__db_status_excluded
+      || status == svn_wc__db_status_not_present)
+    {
+      return svn_error_createf(
+                    SVN_ERR_WC_PATH_NOT_FOUND, NULL,
+                    _("The node '%s' was not found."),
+                    path_for_error_message(wcroot, local_relpath,
+                                           scratch_pool));
+    }
+  else if (! repos_relpath)
+    {
+      /* Node is shadowed; easy out */
+      if (isb->is_switched)
+        *isb->is_switched = FALSE;
+
+      return SVN_NO_ERROR;
+    }
+
+  if (! isb->is_switched)
+    return SVN_NO_ERROR;
+
+  svn_relpath_split(&parent_local_relpath, &name, local_relpath, scratch_pool);
+
+  SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL,
+                                            &parent_repos_relpath,
+                                            &parent_repos_id, NULL, NULL, NULL,
+                                            NULL, NULL, NULL, NULL, NULL, NULL,
+                                            wcroot, parent_local_relpath,
+                                            scratch_pool, scratch_pool));
+
+  if (repos_id != parent_repos_id)
+    *isb->is_switched = TRUE;
+  else
+    {
+      const char *expected_relpath;
+
+      expected_relpath = svn_relpath_join(parent_repos_relpath, name,
+                                          scratch_pool);
+
+      *isb->is_switched = (strcmp(expected_relpath, repos_relpath) != 0);
+    }
+
+  return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_wc__db_is_switched(svn_boolean_t *is_wcroot,
+                       svn_boolean_t *is_switched,
+                       svn_kind_t *kind,
+                       svn_wc__db_t *db,
+                       const char *local_abspath,
+                       apr_pool_t *scratch_pool)
+{
+  svn_wc__db_wcroot_t *wcroot;
+  const char *local_relpath;
+  struct db_is_switched_baton_t isb;
+
+  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
+
+  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
+                              local_abspath, scratch_pool, scratch_pool));
+  VERIFY_USABLE_WCROOT(wcroot);
+
+  if (is_switched)
+    *is_switched = FALSE;
+
+  if (*local_relpath == '\0')
+    {
+      /* Easy out */
+      if (is_wcroot)
+        *is_wcroot = TRUE;
+      
+      if (kind)
+        *kind = svn_kind_dir;
+      return SVN_NO_ERROR;
+    }
+
+  if (is_wcroot)
+    *is_wcroot = FALSE;
+
+  isb.is_switched = is_switched;
+  isb.kind = kind;
+
+  return svn_error_trace(svn_wc__db_with_txn(wcroot, local_relpath,
+                                             db_is_switched, &isb,
+                                             scratch_pool));
+}
+
 
 svn_error_t *
 svn_wc__db_temp_wcroot_tempdir(const char **temp_dir_abspath,

Modified: subversion/branches/issue-4194-dev/subversion/libsvn_wc/wc_db.h
URL: http://svn.apache.org/viewvc/subversion/branches/issue-4194-dev/subversion/libsvn_wc/wc_db.h?rev=1414433&r1=1414432&r2=1414433&view=diff
==============================================================================
--- subversion/branches/issue-4194-dev/subversion/libsvn_wc/wc_db.h (original)
+++ subversion/branches/issue-4194-dev/subversion/libsvn_wc/wc_db.h Tue Nov 27 22:13:24 2012
@@ -512,6 +512,7 @@ svn_wc__db_base_add_file(svn_wc__db_t *d
                          svn_boolean_t delete_working,
                          svn_boolean_t update_actual_props,
                          apr_hash_t *new_actual_props,
+                         apr_array_header_t *new_iprops,
                          svn_boolean_t keep_recorded_info,
                          svn_boolean_t insert_base_deleted,
                          const svn_skel_t *conflict,
@@ -594,6 +595,7 @@ svn_wc__db_base_add_symlink(svn_wc__db_t
                             svn_boolean_t delete_working,
                             svn_boolean_t update_actual_props,
                             apr_hash_t *new_actual_props,
+                            apr_array_header_t *new_iprops,
                             svn_boolean_t keep_recorded_info,
                             svn_boolean_t insert_base_deleted,
                             const svn_skel_t *conflict,
@@ -2092,6 +2094,26 @@ svn_wc__db_read_pristine_props(apr_hash_
                                apr_pool_t *result_pool,
                                apr_pool_t *scratch_pool);
 
+
+/**
+ * Set @a *inherited_props to a depth-first ordered array of
+ * #svn_prop_inherited_item_t * structures representing the properties
+ * inherited by @a local_abspath from the ACTUAL tree above
+ * @a local_abspath (looking through to the WORKING or BASE tree as
+ * required), up to and including the root of the working copy and
+ * any cached inherited properties inherited by the root.
+ *
+ * Allocate @a *inherited_props in @a result_pool.  Use @a scratch_pool
+ * for temporary allocations.
+ */
+svn_error_t *
+svn_wc__db_read_inherited_props(apr_array_header_t **iprops,
+                                svn_wc__db_t *db,
+                                const char *local_abspath,
+                                const char *propname,
+                                apr_pool_t *result_pool,
+                                apr_pool_t *scratch_pool);
+
 /* Read a BASE node's inherited property information.
 
    Set *IPROPS to to a depth-first ordered array of
@@ -2114,9 +2136,11 @@ svn_wc__db_read_cached_iprops(apr_array_
 /* Find BASE nodes with cached inherited properties.
 
    Set *IPROPS_PATHS to a hash mapping const char * absolute working copy
-   paths to the same for each path in the working copy at or below
-   LOCAL_ABSPATH, limited by DEPTH, that has cached inherited properties
-   for the BASE node of the path.  Allocate *IPROP_PATHS in RESULT_POOL.
+   paths to the repos_relpath of the path for each path in the working copy
+   at or below LOCAL_ABSPATH, limited by DEPTH, that has cached inherited
+   properties for the BASE node of the path.
+
+   Allocate *IPROP_PATHS in RESULT_POOL.
    Use SCRATCH_POOL for temporary allocations. */
 svn_error_t *
 svn_wc__db_get_children_with_cached_iprops(apr_hash_t **iprop_paths,
@@ -2288,6 +2312,21 @@ svn_wc__db_is_wcroot(svn_boolean_t *is_r
                      const char *local_abspath,
                      apr_pool_t *scratch_pool);
 
+/* Checks if LOCAL_ABSPATH is a working copy root, switched and a directory.
+   With these answers we can answer all 'is anchor' questions that we need for
+   the different ra operations with just a single sqlite transaction and
+   filestat.
+
+   All output arguments can be null to specify that the result is not
+   interesting to the caller
+ */
+svn_error_t *
+svn_wc__db_is_switched(svn_boolean_t *is_wcroot,
+                       svn_boolean_t *is_switched,
+                       svn_kind_t *kind,
+                       svn_wc__db_t *db,
+                       const char *local_abspath,
+                       apr_pool_t *scratch_pool);
 
 
 /* @} */

Modified: subversion/branches/issue-4194-dev/subversion/mod_dav_svn/reports/update.c
URL: http://svn.apache.org/viewvc/subversion/branches/issue-4194-dev/subversion/mod_dav_svn/reports/update.c?rev=1414433&r1=1414432&r2=1414433&view=diff
==============================================================================
--- subversion/branches/issue-4194-dev/subversion/mod_dav_svn/reports/update.c (original)
+++ subversion/branches/issue-4194-dev/subversion/mod_dav_svn/reports/update.c Tue Nov 27 22:13:24 2012
@@ -618,12 +618,8 @@ send_propchange(item_baton_t *b,
 {
   const char *qname;
 
-  /* Ensure that the property name is XML-safe.
-     apr_xml_quote_string() doesn't realloc if there is nothing to
-     quote, so dup the name, but only if necessary. */
-  qname = apr_xml_quote_string(b->pool, name, 1);
-  if (qname == name)
-    qname = apr_pstrdup(b->pool, name);
+  /* Ensure that the property name is XML-safe. */
+  qname = apr_xml_quote_string(pool, name, 1);
 
   if (value)
     {

Modified: subversion/branches/issue-4194-dev/subversion/svn/cl.h
URL: http://svn.apache.org/viewvc/subversion/branches/issue-4194-dev/subversion/svn/cl.h?rev=1414433&r1=1414432&r2=1414433&view=diff
==============================================================================
--- subversion/branches/issue-4194-dev/subversion/svn/cl.h (original)
+++ subversion/branches/issue-4194-dev/subversion/svn/cl.h Tue Nov 27 22:13:24 2012
@@ -767,6 +767,22 @@ const char *
 svn_cl__operation_str_human_readable(svn_wc_operation_t operation,
                                      apr_pool_t *pool);
 
+/* If PROPNAME looks like but is not identical to one of the svn:
+ * poperties, raise an error and suggest a better spelling. Names that
+ * raise errors look like this:
+ *
+ *   - start with svn: but do not exactly match a known property; or,
+ *   - start with a 3-letter prefix that differs in only one letter
+ *     from "svn:", and the rest exactly matches a known propery.
+ *
+ * If REVPROP is TRUE, only check revision property names; otherwise
+ * only check node property names.
+ *
+ * Use SCRATCH_POOL for temporary allocations.
+ */
+svn_error_t *
+svn_cl__check_svn_prop_name(const char *propname, svn_boolean_t revprop,
+                            apr_pool_t *scratch_pool);
 
 /* If PROPNAME is one of the svn: properties with a boolean value, and
  * PROPVAL looks like an attempt to turn the property off (i.e., it's

Modified: subversion/branches/issue-4194-dev/subversion/svn/list-cmd.c
URL: http://svn.apache.org/viewvc/subversion/branches/issue-4194-dev/subversion/svn/list-cmd.c?rev=1414433&r1=1414432&r2=1414433&view=diff
==============================================================================
--- subversion/branches/issue-4194-dev/subversion/svn/list-cmd.c (original)
+++ subversion/branches/issue-4194-dev/subversion/svn/list-cmd.c Tue Nov 27 22:13:24 2012
@@ -42,9 +42,14 @@
 struct print_baton {
   svn_boolean_t verbose;
   svn_client_ctx_t *ctx;
+  
+  /* To keep track of last seen external information. */
+  const char *last_external_parent_url;
+  const char *last_external_target;
+  svn_boolean_t in_external;
 };
 
-/* This implements the svn_client_list_func_t API, printing a single
+/* This implements the svn_client_list_func2_t API, printing a single
    directory entry in text format. */
 static svn_error_t *
 print_dirent(void *baton,
@@ -52,6 +57,8 @@ print_dirent(void *baton,
              const svn_dirent_t *dirent,
              const svn_lock_t *lock,
              const char *abs_path,
+             const char *external_parent_url,
+             const char *external_target,
              apr_pool_t *pool)
 {
   struct print_baton *pb = baton;
@@ -59,6 +66,9 @@ print_dirent(void *baton,
   static const char *time_format_long = NULL;
   static const char *time_format_short = NULL;
 
+  SVN_ERR_ASSERT((external_parent_url == NULL && external_target == NULL) ||
+                 (external_parent_url && external_target));
+
   if (time_format_long == NULL)
     time_format_long = _("%b %d %H:%M");
   if (time_format_short == NULL)
@@ -79,6 +89,24 @@ print_dirent(void *baton,
     }
   else
     entryname = path;
+  
+  if (external_parent_url && external_target)
+    {
+      if ((pb->last_external_parent_url == NULL 
+           && pb->last_external_target == NULL) 
+          || (strcmp(pb->last_external_parent_url, external_parent_url) != 0
+              || strcmp(pb->last_external_target, external_target) != 0))
+        {
+          SVN_ERR(svn_cmdline_printf(pool,
+                                     _("Listing external '%s'"
+                                       " defined on '%s':\n"), 
+                                     external_target,
+                                     external_parent_url));
+
+          pb->last_external_parent_url = external_parent_url;
+          pb->last_external_target = external_target;
+        }
+    }
 
   if (pb->verbose)
     {
@@ -133,7 +161,7 @@ print_dirent(void *baton,
 }
 
 
-/* This implements the svn_client_list_func_t API, printing a single dirent
+/* This implements the svn_client_list_func2_t API, printing a single dirent
    in XML format. */
 static svn_error_t *
 print_dirent_xml(void *baton,
@@ -141,18 +169,21 @@ print_dirent_xml(void *baton,
                  const svn_dirent_t *dirent,
                  const svn_lock_t *lock,
                  const char *abs_path,
+                 const char *external_parent_url,
+                 const char *external_target,
                  apr_pool_t *pool)
 {
   struct print_baton *pb = baton;
   const char *entryname;
-  svn_stringbuf_t *sb;
+  svn_stringbuf_t *sb = svn_stringbuf_create_empty(pool);
+  
+  SVN_ERR_ASSERT((external_parent_url == NULL && external_target == NULL) ||
+                 (external_parent_url && external_target));
 
   if (strcmp(path, "") == 0)
     {
       if (dirent->kind == svn_node_file)
         entryname = svn_dirent_basename(abs_path, pool);
-      else if (pb->verbose)
-        entryname = ".";
       else
         /* Don't bother to list if no useful information will be shown. */
         return SVN_NO_ERROR;
@@ -162,8 +193,32 @@ print_dirent_xml(void *baton,
 
   if (pb->ctx->cancel_func)
     SVN_ERR(pb->ctx->cancel_func(pb->ctx->cancel_baton));
+  
+  if (external_parent_url && external_target)
+    {
+      if ((pb->last_external_parent_url == NULL 
+           && pb->last_external_target == NULL)
+          || (strcmp(pb->last_external_parent_url, external_parent_url) != 0
+              || strcmp(pb->last_external_target, external_target) != 0))
+        {
+          if (pb->in_external)
+            {
+              /* The external item being listed is different from the previous
+                 one, so close the tag. */
+              svn_xml_make_close_tag(&sb, pool, "external");
+              pb->in_external = FALSE;
+            }
+
+          svn_xml_make_open_tag(&sb, pool, svn_xml_normal, "external",
+                                "parent_url", external_parent_url,
+                                "target", external_target,
+                                NULL);
 
-  sb = svn_stringbuf_create_empty(pool);
+          pb->last_external_parent_url = external_parent_url;
+          pb->last_external_target = external_target;
+          pb->in_external = TRUE;
+        }
+    }
 
   svn_xml_make_open_tag(&sb, pool, svn_xml_normal, "entry",
                         "kind", svn_cl__node_kind_str_xml(dirent->kind),
@@ -225,6 +280,8 @@ svn_cl__list(apr_getopt_t *os,
   struct print_baton pb;
   svn_boolean_t seen_nonexistent_target = FALSE;
   svn_error_t *err;
+  svn_error_t *externals_err = SVN_NO_ERROR;
+  struct svn_cl__check_externals_failed_notify_baton nwb;
 
   SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os,
                                                       opt_state->targets,
@@ -266,12 +323,27 @@ svn_cl__list(apr_getopt_t *os,
   if (opt_state->depth == svn_depth_unknown)
     opt_state->depth = svn_depth_immediates;
 
+  if (opt_state->include_externals)
+    {
+      nwb.wrapped_func = ctx->notify_func2;
+      nwb.wrapped_baton = ctx->notify_baton2;
+      nwb.had_externals_error = FALSE;
+      ctx->notify_func2 = svn_cl__check_externals_failed_notify_wrapper;
+      ctx->notify_baton2 = &nwb;
+    }
+
   /* For each target, try to list it. */
   for (i = 0; i < targets->nelts; i++)
     {
       const char *target = APR_ARRAY_IDX(targets, i, const char *);
       const char *truepath;
       svn_opt_revision_t peg_revision;
+      
+      /* Initialize the following variables for 
+         every list target. */
+      pb.last_external_parent_url = NULL;
+      pb.last_external_target = NULL;
+      pb.in_external = FALSE;
 
       svn_pool_clear(subpool);
 
@@ -290,11 +362,12 @@ svn_cl__list(apr_getopt_t *os,
           SVN_ERR(svn_cl__error_checked_fputs(sb->data, stdout));
         }
 
-      err = svn_client_list2(truepath, &peg_revision,
+      err = svn_client_list3(truepath, &peg_revision,
                              &(opt_state->start_revision),
                              opt_state->depth,
                              dirent_fields,
                              (opt_state->xml || opt_state->verbose),
+                             opt_state->include_externals,
                              opt_state->xml ? print_dirent_xml : print_dirent,
                              &pb, ctx, subpool);
 
@@ -316,20 +389,38 @@ svn_cl__list(apr_getopt_t *os,
       if (opt_state->xml)
         {
           svn_stringbuf_t *sb = svn_stringbuf_create_empty(pool);
+          
+          if (pb.in_external)
+            {
+              /* close the final external item's tag */ 
+              svn_xml_make_close_tag(&sb, pool, "external");
+              pb.in_external = FALSE;
+            }
+
           svn_xml_make_close_tag(&sb, pool, "list");
           SVN_ERR(svn_cl__error_checked_fputs(sb->data, stdout));
         }
     }
 
   svn_pool_destroy(subpool);
+  
+  if (opt_state->include_externals && nwb.had_externals_error)
+    {
+      externals_err = svn_error_create(SVN_ERR_CL_ERROR_PROCESSING_EXTERNALS,
+                                       NULL,
+                                       _("Failure occurred processing one or "
+                                         "more externals definitions"));
+    }
 
   if (opt_state->xml && ! opt_state->incremental)
     SVN_ERR(svn_cl__xml_print_footer("lists", pool));
 
   if (seen_nonexistent_target)
-    return svn_error_create(
-      SVN_ERR_ILLEGAL_TARGET, NULL,
-      _("Could not list all targets because some targets don't exist"));
+    {
+      err = svn_error_create(SVN_ERR_ILLEGAL_TARGET, NULL,
+            _("Could not list all targets because some targets don't exist"));
+      return svn_error_compose_create(externals_err, err);
+    }
   else
-    return SVN_NO_ERROR;
+    return svn_error_compose_create(externals_err, err);
 }

Modified: subversion/branches/issue-4194-dev/subversion/svn/main.c
URL: http://svn.apache.org/viewvc/subversion/branches/issue-4194-dev/subversion/svn/main.c?rev=1414433&r1=1414432&r2=1414433&view=diff
==============================================================================
--- subversion/branches/issue-4194-dev/subversion/svn/main.c (original)
+++ subversion/branches/issue-4194-dev/subversion/svn/main.c Tue Nov 27 22:13:24 2012
@@ -131,7 +131,7 @@ typedef enum svn_cl__longopt_t {
   opt_include_externals,
   opt_show_inherited_props,
   opt_search,
-  opt_search_and,
+  opt_search_and
 } svn_cl__longopt_t;
 
 
@@ -624,7 +624,9 @@ const svn_opt_subcommand_desc2_t svn_cl_
      "    If locked, the letter 'O'.  (Use 'svn info URL' to see details)\n"
      "    Size (in bytes)\n"
      "    Date and time of the last commit\n"),
-    {'r', 'v', 'R', opt_depth, opt_incremental, opt_xml} },
+    {'r', 'v', 'R', opt_depth, opt_incremental, opt_xml, 
+     opt_include_externals },
+    {{opt_include_externals, N_("include externals definitions")}} },
 
   { "lock", svn_cl__lock, {0}, N_
     ("Lock working copy paths or URLs in the repository, so that\n"

Modified: subversion/branches/issue-4194-dev/subversion/svn/propedit-cmd.c
URL: http://svn.apache.org/viewvc/subversion/branches/issue-4194-dev/subversion/svn/propedit-cmd.c?rev=1414433&r1=1414432&r2=1414433&view=diff
==============================================================================
--- subversion/branches/issue-4194-dev/subversion/svn/propedit-cmd.c (original)
+++ subversion/branches/issue-4194-dev/subversion/svn/propedit-cmd.c Tue Nov 27 22:13:24 2012
@@ -84,6 +84,9 @@ svn_cl__propedit(apr_getopt_t *os,
     return svn_error_createf(SVN_ERR_CLIENT_PROPERTY_NAME, NULL,
                              _("'%s' is not a valid Subversion property name"),
                              pname_utf8);
+  if (!opt_state->force)
+    SVN_ERR(svn_cl__check_svn_prop_name(pname_utf8, opt_state->revprop, pool));
+
   if (opt_state->encoding && !svn_prop_needs_translation(pname_utf8))
       return svn_error_create
           (SVN_ERR_UNSUPPORTED_FEATURE, NULL,

Modified: subversion/branches/issue-4194-dev/subversion/svn/props.c
URL: http://svn.apache.org/viewvc/subversion/branches/issue-4194-dev/subversion/svn/props.c?rev=1414433&r1=1414432&r2=1414433&view=diff
==============================================================================
--- subversion/branches/issue-4194-dev/subversion/svn/props.c (original)
+++ subversion/branches/issue-4194-dev/subversion/svn/props.c Tue Nov 27 22:13:24 2012
@@ -27,6 +27,8 @@
 
 /*** Includes. ***/
 
+#include <stdlib.h>
+
 #include <apr_hash.h>
 #include "svn_cmdline.h"
 #include "svn_string.h"
@@ -40,10 +42,10 @@
 #include "svn_base64.h"
 #include "cl.h"
 
+#include "private/svn_string_private.h"
 #include "private/svn_cmdline_private.h"
 
 #include "svn_private_config.h"
-
 
 
 svn_error_t *
@@ -223,3 +225,191 @@ svn_cl__check_boolean_prop_val(const cha
     }
 }
 
+
+/* Context for sorting property names */
+struct simprop_context_t
+{
+  svn_string_t name;    /* The name of the property we're comparing with */
+  svn_membuf_t buffer;  /* Buffer for similariry testing */
+};
+
+struct simprop_t
+{
+  const char *propname; /* The original svn: property name */
+  svn_string_t name;    /* The property name without the svn: prefx */
+  unsigned int score;   /* The similarity score */
+  apr_size_t diff;      /* Number of chars different from context.name */
+  struct simprop_context_t *context; /* Sorting context for qsort() */
+};
+
+/* Similarity test between two property names */
+static APR_INLINE unsigned int
+simprop_key_diff(const svn_string_t *key, const svn_string_t *ctx,
+                 svn_membuf_t *buffer, apr_size_t *diff)
+{
+  apr_size_t lcs;
+  const unsigned int score = svn_string__similarity(key, ctx, buffer, &lcs);
+  if (key->len > ctx->len)
+    *diff = key->len - lcs;
+  else
+    *diff = ctx->len - lcs;
+  return score;
+}
+
+/* Key comparator for qsort for simprop_t */
+static int
+simprop_compare(const void *pkeya, const void *pkeyb)
+{
+  struct simprop_t *const keya = *(struct simprop_t *const *)pkeya;
+  struct simprop_t *const keyb = *(struct simprop_t *const *)pkeyb;
+  struct simprop_context_t *const context = keya->context;
+
+  if (keya->score == -1)
+    keya->score = simprop_key_diff(&keya->name, &context->name,
+                                   &context->buffer, &keya->diff);
+  if (keyb->score == -1)
+    keyb->score = simprop_key_diff(&keyb->name, &context->name,
+                                   &context->buffer, &keyb->diff);
+
+  return (keya->score < keyb->score ? 1
+          : (keya->score > keyb->score ? -1
+             : (keya->diff > keyb->diff ? 1
+                : (keya->diff < keyb->diff ? -1 : 0))));
+}
+
+svn_error_t *
+svn_cl__check_svn_prop_name(const char *propname, svn_boolean_t revprop,
+                            apr_pool_t *scratch_pool)
+{
+  static const char *const nodeprops[] =
+    {
+      SVN_PROP_NODE_ALL_PROPS
+    };
+  static const apr_size_t nodeprops_len = sizeof(nodeprops)/sizeof(*nodeprops);
+
+  static const char *const revprops[] =
+    {
+      SVN_PROP_REVISION_ALL_PROPS
+    };
+  static const apr_size_t revprops_len = sizeof(revprops)/sizeof(*revprops);
+
+  const char *const *const proplist = (revprop ? revprops : nodeprops);
+  const apr_size_t numprops = (revprop ? revprops_len : nodeprops_len);
+
+  struct simprop_t **propkeys;
+  struct simprop_t *propbuf;
+  apr_size_t i;
+
+  struct simprop_context_t context;
+  svn_string_t prefix;
+
+  context.name.data = propname;
+  context.name.len = strlen(propname);
+  prefix.data = SVN_PROP_PREFIX;
+  prefix.len = strlen(SVN_PROP_PREFIX);
+
+  svn_membuf__create(&context.buffer, 0, scratch_pool);
+
+  /* First, check if the name is even close to being in the svn: namespace.
+     It must contain a colon in the right place, and we only allow
+     one-char typos or a single transposition. */
+  if (context.name.len < prefix.len
+      || context.name.data[prefix.len - 1] != prefix.data[prefix.len - 1])
+    return SVN_NO_ERROR;        /* Wrong prefix, ignore */
+  else
+    {
+      apr_size_t lcs;
+      const apr_size_t name_len = context.name.len;
+      context.name.len = prefix.len; /* Only check up to the prefix length */
+      svn_string__similarity(&context.name, &prefix, &context.buffer, &lcs);
+      context.name.len = name_len; /* Restore the original propname length */
+      if (lcs < prefix.len - 1)
+        return SVN_NO_ERROR;    /* Wrong prefix, ignore */
+
+      /* If the prefix is slightly different, the rest must be
+         identical in order to trigger the error. */
+      if (lcs == prefix.len - 1)
+        {
+          for (i = 0; i < numprops; ++i)
+            {
+              if (0 == strcmp(proplist[i] + prefix.len, propname + prefix.len))
+                return svn_error_createf(
+                  SVN_ERR_CLIENT_PROPERTY_NAME, NULL,
+                  _("'%s' is not a valid %s property name; did you mean '%s'?"
+                    "\n(To set the '%s' property, re-run with '--force'.)"),
+                  propname, SVN_PROP_PREFIX, proplist[i], propname);
+            }
+          return SVN_NO_ERROR;
+        }
+    }
+
+  /* Now find the closest match from amongst a the set of reserved
+     node or revision property names. Skip the prefix while matching,
+     we already know that it's the same and looking at it would only
+     skew the results. */
+  propkeys = apr_palloc(scratch_pool,
+                        numprops * sizeof(struct simprop_t*));
+  propbuf = apr_palloc(scratch_pool,
+                       numprops * sizeof(struct simprop_t));
+  context.name.data += prefix.len;
+  context.name.len -= prefix.len;
+  for (i = 0; i < numprops; ++i)
+    {
+      propkeys[i] = &propbuf[i];
+      propbuf[i].propname = proplist[i];
+      propbuf[i].name.data = proplist[i] + prefix.len;
+      propbuf[i].name.len = strlen(propbuf[i].name.data);
+      propbuf[i].score = (unsigned int)-1;
+      propbuf[i].context = &context;
+    }
+
+  qsort(propkeys, numprops, sizeof(*propkeys), simprop_compare);
+
+  if (0 == propkeys[0]->diff)
+    return SVN_NO_ERROR;        /* We found an exact match. */
+
+  /* See if we can suggest a sane alternative spelling */
+  for (i = 0; i < numprops; ++i)
+    if (propkeys[i]->score < 666) /* 2/3 similarity required */
+      break;
+
+  switch (i)
+    {
+    case 0:
+      /* The best alternative isn't good enough */
+      return svn_error_createf(
+        SVN_ERR_CLIENT_PROPERTY_NAME, NULL,
+        _("'%s' is not a valid %s property name;"
+          " re-run with '--force' to set it"),
+        propname, SVN_PROP_PREFIX);
+
+    case 1:
+      /* There is only one good candidate */
+      return svn_error_createf(
+        SVN_ERR_CLIENT_PROPERTY_NAME, NULL,
+        _("'%s' is not a valid %s property name; did you mean '%s'?\n"
+          "(To set the '%s' property, re-run with '--force'.)"),
+        propname, SVN_PROP_PREFIX, propkeys[0]->propname, propname);
+
+    case 2:
+      /* Suggest a list of the most likely candidates */
+      return svn_error_createf(
+        SVN_ERR_CLIENT_PROPERTY_NAME, NULL,
+        _("'%s' is not a valid %s property name\n"
+          "Did you mean '%s' or '%s'?\n"
+          "(To set the '%s' property, re-run with '--force'.)"),
+        propname, SVN_PROP_PREFIX,
+        propkeys[0]->propname, propkeys[1]->propname, propname);
+
+    default:
+      /* Never suggest more than three candidates */
+      return svn_error_createf(
+        SVN_ERR_CLIENT_PROPERTY_NAME, NULL,
+        _("'%s' is not a valid %s property name\n"
+          "Did you mean '%s', '%s' or '%s'?\n"
+          "(To set the '%s' property, re-run with '--force'.)"),
+        propname, SVN_PROP_PREFIX,
+        propkeys[0]->propname, propkeys[1]->propname, propkeys[2]->propname,
+        propname);
+    }
+}

Modified: subversion/branches/issue-4194-dev/subversion/svn/propset-cmd.c
URL: http://svn.apache.org/viewvc/subversion/branches/issue-4194-dev/subversion/svn/propset-cmd.c?rev=1414433&r1=1414432&r2=1414433&view=diff
==============================================================================
--- subversion/branches/issue-4194-dev/subversion/svn/propset-cmd.c (original)
+++ subversion/branches/issue-4194-dev/subversion/svn/propset-cmd.c Tue Nov 27 22:13:24 2012
@@ -67,6 +67,9 @@ svn_cl__propset(apr_getopt_t *os,
     return svn_error_createf(SVN_ERR_CLIENT_PROPERTY_NAME, NULL,
                              _("'%s' is not a valid Subversion property name"),
                              pname_utf8);
+  if (!opt_state->force)
+    SVN_ERR(svn_cl__check_svn_prop_name(pname_utf8, opt_state->revprop,
+                                        scratch_pool));
 
   /* Get the PROPVAL from either an external file, or from the command
      line. */

Modified: subversion/branches/issue-4194-dev/subversion/svnlook/main.c
URL: http://svn.apache.org/viewvc/subversion/branches/issue-4194-dev/subversion/svnlook/main.c?rev=1414433&r1=1414432&r2=1414433&view=diff
==============================================================================
--- subversion/branches/issue-4194-dev/subversion/svnlook/main.c (original)
+++ subversion/branches/issue-4194-dev/subversion/svnlook/main.c Tue Nov 27 22:13:24 2012
@@ -52,6 +52,7 @@
 #include "svn_version.h"
 #include "svn_xml.h"
 
+#include "private/svn_diff_private.h"
 #include "private/svn_cmdline_private.h"
 #include "private/svn_fspath.h"
 
@@ -95,7 +96,8 @@ enum
     svnlook__copy_info,
     svnlook__xml_opt,
     svnlook__ignore_properties,
-    svnlook__properties_only
+    svnlook__properties_only,
+    svnlook__diff_cmd
   };
 
 /*
@@ -127,6 +129,9 @@ static const apr_getopt_option_t options
   {"no-diff-deleted",   svnlook__no_diff_deleted, 0,
    N_("do not print differences for deleted files")},
 
+  {"diff-cmd",          svnlook__diff_cmd, 1,
+   N_("use ARG as diff command")},
+
   {"ignore-properties",   svnlook__ignore_properties, 0,
    N_("ignore properties during the operation")},
 
@@ -158,37 +163,23 @@ static const apr_getopt_option_t options
    N_("output in XML")},
 
   {"extensions",        'x', 1,
-   N_("Default: '-u'. When Subversion is invoking an\n"
-      "                            "
-      " external diff program, ARG is simply passed along\n"
-      "                            "
-      " to the program. But when Subversion is using its\n"
-      "                            "
-      " default internal diff implementation, or when\n"
-      "                            "
-      " Subversion is displaying blame annotations, ARG\n"
-      "                            "
-      " could be any of the following:\n"
-      "                            "
-      "    -u (--unified):\n"
-      "                            "
-      "       Output 3 lines of unified context.\n"
-      "                            "
-      "    -b (--ignore-space-change):\n"
-      "                            "
-      "       Ignore changes in the amount of white space.\n"
-      "                            "
-      "    -w (--ignore-all-space):\n"
-      "                            "
-      "       Ignore all white space.\n"
-      "                            "
-      "    --ignore-eol-style:\n"
-      "                            "
-      "       Ignore changes in EOL style\n"
-      "                            "
-      "    -p (--show-c-function):\n"
-      "                            "
-      "       Show C function name in diff output.")},
+   N_("Specify differencing options for external diff or\n"
+      "                             "
+      "internal diff. Default: '-u'. Options are\n"
+      "                             "
+      "separated by spaces. Internal diff takes:\n"
+      "                             "
+      "  -u, --unified: Show 3 lines of unified context\n"
+      "                             "
+      "  -b, --ignore-space-change: Ignore changes in\n"
+      "                             "
+      "    amount of white space\n"
+      "                             "
+      "  -w, --ignore-all-space: Ignore all white space\n"
+      "                             "
+      "  --ignore-eol-style: Ignore changes in EOL style\n"
+      "                             "
+      "  -p, --show-c-function: Show C function name")},
 
   {"quiet",             'q', 0,
    N_("no progress (only errors) to stderr")},
@@ -226,8 +217,8 @@ static const svn_opt_subcommand_desc2_t 
    N_("usage: svnlook diff REPOS_PATH\n\n"
       "Print GNU-style diffs of changed files and properties.\n"),
    {'r', 't', svnlook__no_diff_deleted, svnlook__no_diff_added,
-    svnlook__diff_copy_from, 'x', svnlook__ignore_properties,
-    svnlook__properties_only} },
+    svnlook__diff_copy_from, svnlook__diff_cmd, 'x',
+    svnlook__ignore_properties, svnlook__properties_only} },
 
   {"dirs-changed", subcommand_dirschanged, {0},
    N_("usage: svnlook dirs-changed REPOS_PATH\n\n"
@@ -331,6 +322,7 @@ struct svnlook_opt_state
   svn_boolean_t quiet;            /* --quiet */
   svn_boolean_t ignore_properties;  /* --ignore_properties */
   svn_boolean_t properties_only;    /* --properties-only */
+  const char *diff_cmd;           /* --diff-cmd */
 };
 
 
@@ -352,6 +344,7 @@ typedef struct svnlook_ctxt_t
   const apr_array_header_t *diff_options;
   svn_boolean_t ignore_properties;
   svn_boolean_t properties_only;
+  const char *diff_cmd;
 
 } svnlook_ctxt_t;
 
@@ -791,66 +784,16 @@ generate_label(const char **label,
   return SVN_NO_ERROR;
 }
 
-/* A helper function used by display_prop_diffs.
-   TOKEN is a string holding a property value.
-   If TOKEN is empty, or is already terminated by an EOL marker,
-   return TOKEN unmodified. Else, return a new string consisting
-   of the concatenation of TOKEN and the system's default EOL marker.
-   The new string is allocated from POOL.
-   If HAD_EOL is not NULL, indicate in *HAD_EOL if the token had a EOL. */
-static const svn_string_t *
-maybe_append_eol(const svn_string_t *token, svn_boolean_t *had_eol,
-                 apr_pool_t *pool)
-{
-  const char *curp;
-
-  if (had_eol)
-    *had_eol = FALSE;
-
-  if (token->len == 0)
-    return token;
-
-  curp = token->data + token->len - 1;
-  if (*curp == '\r')
-    {
-      if (had_eol)
-        *had_eol = TRUE;
-      return token;
-    }
-  else if (*curp != '\n')
-    {
-      return svn_string_createf(pool, "%s%s", token->data, APR_EOL_STR);
-    }
-  else
-    {
-      if (had_eol)
-        *had_eol = TRUE;
-      return token;
-    }
-}
-
-/*
- * Constant diff output separator strings
- */
-static const char equal_string[] =
-  "===================================================================";
-static const char under_string[] =
-  "___________________________________________________________________";
-
 
 /* Helper function to display differences in properties of a file */
 static svn_error_t *
-display_prop_diffs(const apr_array_header_t *propchanges,
+display_prop_diffs(svn_stream_t *outstream,
+                   const char *encoding,
+                   const apr_array_header_t *propchanges,
                    apr_hash_t *original_props,
                    const char *path,
                    apr_pool_t *pool)
 {
-  const char *encoding = svn_cmdline_output_encoding(pool);
-  svn_stream_t *outstream;
-  apr_pool_t *iterpool;
-  int i;
-
-  SVN_ERR(svn_stream_for_stdout(&outstream, pool));
 
   SVN_ERR(svn_stream_printf_from_utf8(outstream, encoding, pool,
                                       _("%sProperty changes on: %s%s"),
@@ -859,90 +802,24 @@ display_prop_diffs(const apr_array_heade
                                       APR_EOL_STR));
 
   SVN_ERR(svn_stream_printf_from_utf8(outstream, encoding, pool,
-                                      "%s" APR_EOL_STR, under_string));
-
-  iterpool = svn_pool_create(pool);
-  for (i = 0; i < propchanges->nelts; i++)
-    {
-      const char *action;
-      const svn_string_t *original_value;
-      const svn_prop_t *propchange
-        = &APR_ARRAY_IDX(propchanges, i, svn_prop_t);
-
-      SVN_ERR(check_cancel(NULL));
+                                      SVN_DIFF__UNDER_STRING APR_EOL_STR));
 
-      if (original_props)
-        original_value = apr_hash_get(original_props,
-                                      propchange->name, APR_HASH_KEY_STRING);
-      else
-        original_value = NULL;
-
-      svn_pool_clear(iterpool);
+  SVN_ERR(check_cancel(NULL));
 
-      if (! original_value)
-        action = "Added";
-      else if (! propchange->value)
-        action = "Deleted";
-      else
-        action = "Modified";
-      SVN_ERR(svn_stream_printf_from_utf8(outstream, encoding, iterpool,
-                                          "%s: %s%s", action,
-                                          propchange->name, APR_EOL_STR));
-
-      {
-        svn_diff_t *diff;
-        svn_diff_file_options_t options;
-        const svn_string_t *tmp;
-        const svn_string_t *orig;
-        const svn_string_t *val;
-        svn_boolean_t val_has_eol;
-
-        /* The last character in a property is often not a newline.
-           An eol character is appended to prevent the diff API to add a
-           '\ No newline at end of file' line. We add
-           '\ No newline at end of property' manually if needed. */
-        tmp = original_value ? original_value
-                             : svn_string_create_empty(iterpool);
-        orig = maybe_append_eol(tmp, NULL, iterpool);
-
-        tmp = propchange->value ? propchange->value :
-                                  svn_string_create_empty(iterpool);
-        val = maybe_append_eol(tmp, &val_has_eol, iterpool);
-
-        SVN_ERR(svn_diff_mem_string_diff(&diff, orig, val, &options,
-                                         iterpool));
-
-        /* UNIX patch will try to apply a diff even if the diff header
-         * is missing. It tries to be helpful by asking the user for a
-         * target filename when it can't determine the target filename
-         * from the diff header. But there usually are no files which
-         * UNIX patch could apply the property diff to, so we use "##"
-         * instead of "@@" as the default hunk delimiter for property diffs.
-         * We also supress the diff header. */
-        SVN_ERR(svn_diff_mem_string_output_unified2(
-                  outstream, diff, FALSE, "##",
-                  svn_dirent_local_style(path, iterpool),
-                  svn_dirent_local_style(path, iterpool),
-                  encoding, orig, val, iterpool));
-        if (!val_has_eol)
-          {
-            const char *s = "\\ No newline at end of property" APR_EOL_STR;
-            SVN_ERR(svn_stream_puts(outstream, s));
-          }
-      }
-    }
-  svn_pool_destroy(iterpool);
+  SVN_ERR(svn_diff__display_prop_diffs(
+            outstream, encoding, propchanges, original_props,
+            FALSE /* pretty_print_mergeinfo */, pool));
 
-  SVN_ERR(svn_stream_close(outstream));
-  return svn_cmdline_fflush(stdout);
+  return SVN_NO_ERROR;
 }
 
 
-
 /* Recursively print all nodes in the tree that have been modified
    (do not include directories affected only by "bubble-up"). */
 static svn_error_t *
-print_diff_tree(svn_fs_root_t *root,
+print_diff_tree(svn_stream_t *out_stream,
+                const char *encoding,
+                svn_fs_root_t *root,
                 svn_fs_root_t *base_root,
                 svn_repos_node_t *node,
                 const char *path /* UTF-8! */,
@@ -1063,59 +940,46 @@ print_diff_tree(svn_fs_root_t *root,
 
   if (do_diff && (! c->properties_only))
     {
-      svn_stringbuf_appendcstr(header, equal_string);
-      svn_stringbuf_appendcstr(header, "\n");
+      svn_stringbuf_appendcstr(header, SVN_DIFF__EQUAL_STRING "\n");
 
       if (binary)
         {
           svn_stringbuf_appendcstr(header, _("(Binary files differ)\n\n"));
-          SVN_ERR(svn_cmdline_printf(pool, "%s", header->data));
+          SVN_ERR(svn_stream_printf_from_utf8(out_stream, encoding, pool,
+                                              "%s", header->data));
         }
       else
         {
-          svn_diff_t *diff;
-          svn_diff_file_options_t *opts = svn_diff_file_options_create(pool);
-
-          if (c->diff_options)
-            SVN_ERR(svn_diff_file_options_parse(opts, c->diff_options, pool));
-
-          SVN_ERR(svn_diff_file_diff_2(&diff, orig_path,
-                                       new_path, opts, pool));
-
-          if (svn_diff_contains_diffs(diff))
+          if (c->diff_cmd)
             {
-              svn_stream_t *ostream;
-              const char *orig_label, *new_label;
+              apr_file_t *outfile;
+              apr_file_t *errfile;
+              const char *outfilename;
+              const char *errfilename;
+              svn_stream_t *stream;
+              svn_stream_t *err_stream;
+              const char **diff_cmd_argv;
+              int diff_cmd_argc;
+              int exitcode;
+              const char *orig_label;
+              const char *new_label;
+
+              diff_cmd_argv = NULL;
+              diff_cmd_argc = c->diff_options->nelts;
+              if (diff_cmd_argc)
+                {
+                  int i;
+                  diff_cmd_argv = apr_palloc(pool, 
+                                             diff_cmd_argc * sizeof(char *));
+                  for (i = 0; i < diff_cmd_argc; i++)
+                    SVN_ERR(svn_utf_cstring_to_utf8(&diff_cmd_argv[i],
+                              APR_ARRAY_IDX(c->diff_options, i, const char *),
+                              pool));
+                }
 
               /* Print diff header. */
-              SVN_ERR(svn_cmdline_printf(pool, "%s", header->data));
-
-              /* This fflush() might seem odd, but it was added to deal
-                 with this bug report:
-
-                   http://subversion.tigris.org/servlets/ReadMsg?\
-                   list=dev&msgNo=140782
-
-                   From: "Steve Hay" <SteveHay{_AT_}planit.com>
-                   To: <de...@subversion.tigris.org>
-                   Subject: svnlook diff output in wrong order when redirected
-                   Date: Fri, 4 Jul 2008 16:34:15 +0100
-                   Message-ID: <1B32FF956ABF414C9BCE5E487A1497E702014F62@\
-                                ukmail02.planit.group>
-
-                 Adding the fflush() fixed the bug (not everyone could
-                 reproduce it, but those who could confirmed the fix).
-                 Later in the thread, Daniel Shahaf speculated as to
-                 why the fix works:
-
-                   "Because svn_cmdline_printf() uses the standard
-                    'FILE *stdout' to write to stdout, while
-                    svn_stream_for_stdout() uses (through
-                    apr_file_open_stdout()) Windows API's to get a
-                    handle for stdout?" */
-              SVN_ERR(svn_cmdline_fflush(stdout));
-
-              SVN_ERR(svn_stream_for_stdout(&ostream, pool));
+              SVN_ERR(svn_stream_printf_from_utf8(out_stream, encoding, pool,
+                                                  "%s", header->data));
 
               if (orig_empty)
                 SVN_ERR(generate_label(&orig_label, NULL, path, pool));
@@ -1123,26 +987,89 @@ print_diff_tree(svn_fs_root_t *root,
                 SVN_ERR(generate_label(&orig_label, base_root,
                                        base_path, pool));
               SVN_ERR(generate_label(&new_label, root, path, pool));
-              SVN_ERR(svn_diff_file_output_unified3
-                      (ostream, diff, orig_path, new_path,
-                       orig_label, new_label,
-                       svn_cmdline_output_encoding(pool), NULL,
-                       opts->show_c_function, pool));
-              SVN_ERR(svn_stream_close(ostream));
-              SVN_ERR(svn_cmdline_printf(pool, "\n"));
+
+              /* We deal in streams, but svn_io_run_diff2() deals in file
+                 handles, unfortunately, so we need to make these temporary
+                 files, and then copy the contents to our stream. */
+              SVN_ERR(svn_io_open_unique_file3(&outfile, &outfilename, NULL,
+                        svn_io_file_del_on_pool_cleanup, pool, pool));
+              SVN_ERR(svn_io_open_unique_file3(&errfile, &errfilename, NULL,
+                        svn_io_file_del_on_pool_cleanup, pool, pool));
+
+              SVN_ERR(svn_io_run_diff2(".",
+                                       diff_cmd_argv,
+                                       diff_cmd_argc,
+                                       orig_label, new_label,
+                                       orig_path, new_path,
+                                       &exitcode, outfile, errfile,
+                                       c->diff_cmd, pool));
+
+              SVN_ERR(svn_io_file_close(outfile, pool));
+              SVN_ERR(svn_io_file_close(errfile, pool));
+
+              /* Now, open and copy our files to our output streams. */
+              SVN_ERR(svn_stream_for_stderr(&err_stream, pool));
+              SVN_ERR(svn_stream_open_readonly(&stream, outfilename,
+                                               pool, pool));
+              SVN_ERR(svn_stream_copy3(stream,
+                                       svn_stream_disown(out_stream, pool),
+                                       NULL, NULL, pool));
+              SVN_ERR(svn_stream_open_readonly(&stream, errfilename,
+                                               pool, pool));
+              SVN_ERR(svn_stream_copy3(stream,
+                                       svn_stream_disown(err_stream, pool),
+                                       NULL, NULL, pool));
+
+              SVN_ERR(svn_stream_printf_from_utf8(out_stream, encoding, pool,
+                                                  "\n"));
               diff_header_printed = TRUE;
             }
-          else if (! node->prop_mod &&
-                  ((! c->no_diff_added && node->action == 'A') ||
-                   (! c->no_diff_deleted && node->action == 'D')))
+          else
             {
-              /* There was an empty file added or deleted in this revision.
-               * We can't print a diff, but we can at least print
-               * a diff header since we know what happened to this file. */
-              SVN_ERR(svn_cmdline_printf(pool, "%s", header->data));
+              svn_diff_t *diff;
+              svn_diff_file_options_t *opts = svn_diff_file_options_create(pool);
+
+              if (c->diff_options)
+                SVN_ERR(svn_diff_file_options_parse(opts, c->diff_options, pool));
+
+              SVN_ERR(svn_diff_file_diff_2(&diff, orig_path,
+                                           new_path, opts, pool));
+
+              if (svn_diff_contains_diffs(diff))
+                {
+                  const char *orig_label, *new_label;
+
+                  /* Print diff header. */
+                  SVN_ERR(svn_stream_printf_from_utf8(out_stream, encoding, pool,
+                                                      "%s", header->data));
+
+                  if (orig_empty)
+                    SVN_ERR(generate_label(&orig_label, NULL, path, pool));
+                  else
+                    SVN_ERR(generate_label(&orig_label, base_root,
+                                           base_path, pool));
+                  SVN_ERR(generate_label(&new_label, root, path, pool));
+                  SVN_ERR(svn_diff_file_output_unified3
+                          (out_stream, diff, orig_path, new_path,
+                           orig_label, new_label,
+                           svn_cmdline_output_encoding(pool), NULL,
+                           opts->show_c_function, pool));
+                  SVN_ERR(svn_stream_printf_from_utf8(out_stream, encoding, pool,
+                                                      "\n"));
+                  diff_header_printed = TRUE;
+                }
+              else if (! node->prop_mod &&
+                      ((! c->no_diff_added && node->action == 'A') ||
+                       (! c->no_diff_deleted && node->action == 'D')))
+                {
+                  /* There was an empty file added or deleted in this revision.
+                   * We can't print a diff, but we can at least print
+                   * a diff header since we know what happened to this file. */
+                  SVN_ERR(svn_stream_printf_from_utf8(out_stream, encoding, pool,
+                                                      "%s", header->data));
+                }
             }
         }
-      SVN_ERR(svn_cmdline_fflush(stdout));
     }
 
   /* Make sure we delete any temporary files. */
@@ -1182,12 +1109,17 @@ print_diff_tree(svn_fs_root_t *root,
                                      pool));
               SVN_ERR(generate_label(&new_label, root, path, pool));
 
-              SVN_ERR(svn_cmdline_printf(pool, "Index: %s\n", path));
-              SVN_ERR(svn_cmdline_printf(pool, "%s\n", equal_string));
-              SVN_ERR(svn_cmdline_printf(pool, "--- %s\n", orig_label));
-              SVN_ERR(svn_cmdline_printf(pool, "+++ %s\n", new_label));
+              SVN_ERR(svn_stream_printf_from_utf8(out_stream, encoding, pool,
+                                                  "Index: %s\n", path));
+              SVN_ERR(svn_stream_printf_from_utf8(out_stream, encoding, pool,
+                                                  SVN_DIFF__EQUAL_STRING "\n"));
+              /* --- <label1>
+               * +++ <label2> */
+              SVN_ERR(svn_diff__unidiff_write_header(
+                        out_stream, encoding, orig_label, new_label, pool));
             }
-          SVN_ERR(display_prop_diffs(props, base_proptable, path, pool));
+          SVN_ERR(display_prop_diffs(out_stream, encoding,
+                                     props, base_proptable, path, pool));
         }
     }
 
@@ -1198,7 +1130,7 @@ print_diff_tree(svn_fs_root_t *root,
 
   /* Recursively handle the node's children. */
   subpool = svn_pool_create(pool);
-  SVN_ERR(print_diff_tree(root, base_root, node,
+  SVN_ERR(print_diff_tree(out_stream, encoding, root, base_root, node,
                           svn_dirent_join(path, node->name, subpool),
                           svn_dirent_join(base_path, node->name, subpool),
                           c, tmpdir, subpool));
@@ -1206,7 +1138,7 @@ print_diff_tree(svn_fs_root_t *root,
     {
       svn_pool_clear(subpool);
       node = node->sibling;
-      SVN_ERR(print_diff_tree(root, base_root, node,
+      SVN_ERR(print_diff_tree(out_stream, encoding, root, base_root, node,
                               svn_dirent_join(path, node->name, subpool),
                               svn_dirent_join(base_path, node->name, subpool),
                               c, tmpdir, subpool));
@@ -1569,12 +1501,40 @@ do_diff(svnlook_ctxt_t *c, apr_pool_t *p
   if (tree)
     {
       const char *tmpdir;
+      svn_stream_t *out_stream;
+      const char *encoding = svn_cmdline_output_encoding(pool);
 
       SVN_ERR(svn_fs_revision_root(&base_root, c->fs, base_rev_id, pool));
       SVN_ERR(svn_io_temp_dir(&tmpdir, pool));
 
-      SVN_ERR(print_diff_tree(root, base_root, tree, "", "",
-                              c, tmpdir, pool));
+      /* This fflush() might seem odd, but it was added to deal
+         with this bug report:
+
+         http://subversion.tigris.org/servlets/ReadMsg?\
+         list=dev&msgNo=140782
+
+         From: "Steve Hay" <SteveHay{_AT_}planit.com>
+         To: <de...@subversion.tigris.org>
+         Subject: svnlook diff output in wrong order when redirected
+         Date: Fri, 4 Jul 2008 16:34:15 +0100
+         Message-ID: <1B32FF956ABF414C9BCE5E487A1497E702014F62@\
+                     ukmail02.planit.group>
+
+         Adding the fflush() fixed the bug (not everyone could
+         reproduce it, but those who could confirmed the fix).
+         Later in the thread, Daniel Shahaf speculated as to
+         why the fix works:
+
+         "Because svn_cmdline_printf() uses the standard
+         'FILE *stdout' to write to stdout, while
+         svn_stream_for_stdout() uses (through
+         apr_file_open_stdout()) Windows API's to get a
+         handle for stdout?" */
+      SVN_ERR(svn_cmdline_fflush(stdout));
+      SVN_ERR(svn_stream_for_stdout(&out_stream, pool));
+
+      SVN_ERR(print_diff_tree(out_stream, encoding, root, base_root, tree,
+                              "", "", c, tmpdir, pool));
     }
   return SVN_NO_ERROR;
 }
@@ -1945,6 +1905,7 @@ get_ctxt_baton(svnlook_ctxt_t **baton_p,
                                           " \t\n\r", TRUE, pool);
   baton->ignore_properties = opt_state->ignore_properties;
   baton->properties_only = opt_state->properties_only;
+  baton->diff_cmd = opt_state->diff_cmd;
 
   if (baton->txn_name)
     SVN_ERR(svn_fs_open_txn(&(baton->txn), baton->fs,
@@ -2454,6 +2415,10 @@ main(int argc, const char *argv[])
           opt_state.properties_only = TRUE;
           break;
 
+        case svnlook__diff_cmd:
+          opt_state.diff_cmd = opt_arg;
+          break;
+
         default:
           SVN_INT_ERR(subcommand_help(NULL, NULL, pool));
           svn_pool_destroy(pool);

Modified: subversion/branches/issue-4194-dev/subversion/svnmucc/svnmucc.c
URL: http://svn.apache.org/viewvc/subversion/branches/issue-4194-dev/subversion/svnmucc/svnmucc.c?rev=1414433&r1=1414432&r2=1414433&view=diff
==============================================================================
--- subversion/branches/issue-4194-dev/subversion/svnmucc/svnmucc.c (original)
+++ subversion/branches/issue-4194-dev/subversion/svnmucc/svnmucc.c Tue Nov 27 22:13:24 2012
@@ -926,7 +926,7 @@ usage(apr_pool_t *pool, int exit_val)
     "  -X, --extra-args ARG  append arguments from file ARG (one per line;\n"
     "                        use \"-\" to read from standard input)\n"
     "  --config-dir ARG      use ARG to override the config directory\n"
-    "  --config-option ARG   use ARG so override a configuration option\n"
+    "  --config-option ARG   use ARG to override a configuration option\n"
     "  --no-auth-cache       do not cache authentication tokens\n"
     "  --version             print version information\n";
   svn_error_clear(svn_cmdline_fputs(msg, stream, pool));

Modified: subversion/branches/issue-4194-dev/subversion/svnrdump/svnrdump.c
URL: http://svn.apache.org/viewvc/subversion/branches/issue-4194-dev/subversion/svnrdump/svnrdump.c?rev=1414433&r1=1414432&r2=1414433&view=diff
==============================================================================
--- subversion/branches/issue-4194-dev/subversion/svnrdump/svnrdump.c (original)
+++ subversion/branches/issue-4194-dev/subversion/svnrdump/svnrdump.c Tue Nov 27 22:13:24 2012
@@ -39,6 +39,7 @@
 #include "svnrdump.h"
 
 #include "private/svn_cmdline_private.h"
+#include "private/svn_ra_private.h"
 
 
 
@@ -248,6 +249,81 @@ replay_revend(svn_revnum_t revision,
   return SVN_NO_ERROR;
 }
 
+#ifdef USE_EV2_IMPL
+/* Print dumpstream-formatted information about REVISION.
+ * Implements the `svn_ra_replay_revstart_callback_t' interface.
+ */
+static svn_error_t *
+replay_revstart_v2(svn_revnum_t revision,
+                   void *replay_baton,
+                   svn_editor_t **editor,
+                   apr_hash_t *rev_props,
+                   apr_pool_t *pool)
+{
+  struct replay_baton *rb = replay_baton;
+  apr_hash_t *normal_props;
+  svn_stringbuf_t *propstring;
+  svn_stream_t *stdout_stream;
+  svn_stream_t *revprop_stream;
+
+  SVN_ERR(svn_stream_for_stdout(&stdout_stream, pool));
+
+  /* Revision-number: 19 */
+  SVN_ERR(svn_stream_printf(stdout_stream, pool,
+                            SVN_REPOS_DUMPFILE_REVISION_NUMBER
+                            ": %ld\n", revision));
+  SVN_ERR(svn_rdump__normalize_props(&normal_props, rev_props, pool));
+  propstring = svn_stringbuf_create_ensure(0, pool);
+  revprop_stream = svn_stream_from_stringbuf(propstring, pool);
+  SVN_ERR(svn_hash_write2(normal_props, revprop_stream, "PROPS-END", pool));
+  SVN_ERR(svn_stream_close(revprop_stream));
+
+  /* Prop-content-length: 13 */
+  SVN_ERR(svn_stream_printf(stdout_stream, pool,
+                            SVN_REPOS_DUMPFILE_PROP_CONTENT_LENGTH
+                            ": %" APR_SIZE_T_FMT "\n", propstring->len));
+
+  /* Content-length: 29 */
+  SVN_ERR(svn_stream_printf(stdout_stream, pool,
+                            SVN_REPOS_DUMPFILE_CONTENT_LENGTH
+                            ": %" APR_SIZE_T_FMT "\n\n", propstring->len));
+
+  /* Property data. */
+  SVN_ERR(svn_stream_write(stdout_stream, propstring->data,
+                           &(propstring->len)));
+
+  SVN_ERR(svn_stream_puts(stdout_stream, "\n"));
+  SVN_ERR(svn_stream_close(stdout_stream));
+
+  SVN_ERR(svn_rdump__get_dump_editor_v2(editor, revision,
+                                        rb->stdout_stream,
+                                        rb->extra_ra_session,
+                                        check_cancel, NULL, pool, pool));
+
+  return SVN_NO_ERROR;
+}
+
+/* Print progress information about the dump of REVISION.
+   Implements the `svn_ra_replay_revfinish_callback_t' interface. */
+static svn_error_t *
+replay_revend_v2(svn_revnum_t revision,
+                 void *replay_baton,
+                 svn_editor_t *editor,
+                 apr_hash_t *rev_props,
+                 apr_pool_t *pool)
+{
+  /* No resources left to free. */
+  struct replay_baton *rb = replay_baton;
+
+  SVN_ERR(svn_editor_complete(editor));
+
+  if (! rb->quiet)
+    SVN_ERR(svn_cmdline_fprintf(stderr, pool, "* Dumped revision %lu.\n",
+                                revision));
+  return SVN_NO_ERROR;
+}
+#endif
+
 /* Initialize the RA layer, and set *CTX to a new client context baton
  * allocated from POOL.  Use CONFIG_DIR and pass USERNAME, PASSWORD,
  * CONFIG_DIR and NO_AUTH_CACHE to initialize the authorization baton.
@@ -391,9 +467,16 @@ replay_revisions(svn_ra_session_t *sessi
 
   if (incremental)
     {
+#ifndef USE_EV2_IMPL
       SVN_ERR(svn_ra_replay_range(session, start_revision, end_revision,
                                   0, TRUE, replay_revstart, replay_revend,
                                   replay_baton, pool));
+#else
+      SVN_ERR(svn_ra__replay_range_ev2(session, start_revision, end_revision,
+                                       0, TRUE, replay_revstart_v2,
+                                       replay_revend_v2, replay_baton,
+                                       NULL, NULL, NULL, NULL, pool));
+#endif
     }
   else
     {
@@ -432,9 +515,16 @@ replay_revisions(svn_ra_session_t *sessi
 
       /* Now go pick up additional revisions in the range, if any. */
       if (start_revision <= end_revision)
+#ifndef USE_EV2_IMPL
         SVN_ERR(svn_ra_replay_range(session, start_revision, end_revision,
                                     0, TRUE, replay_revstart, replay_revend,
                                     replay_baton, pool));
+#else
+      SVN_ERR(svn_ra__replay_range_ev2(session, start_revision, end_revision,
+                                       0, TRUE, replay_revstart_v2,
+                                       replay_revend_v2, replay_baton,
+                                       NULL, NULL, NULL, NULL, pool));
+#endif
     }
 
   SVN_ERR(svn_stream_close(stdout_stream));

Modified: subversion/branches/issue-4194-dev/subversion/svnrdump/svnrdump.h
URL: http://svn.apache.org/viewvc/subversion/branches/issue-4194-dev/subversion/svnrdump/svnrdump.h?rev=1414433&r1=1414432&r2=1414433&view=diff
==============================================================================
--- subversion/branches/issue-4194-dev/subversion/svnrdump/svnrdump.h (original)
+++ subversion/branches/issue-4194-dev/subversion/svnrdump/svnrdump.h Tue Nov 27 22:13:24 2012
@@ -53,6 +53,17 @@ svn_rdump__get_dump_editor(const svn_del
                            void *cancel_baton,
                            apr_pool_t *pool);
 
+/* Same as above, only returns an Ev2 editor. */
+svn_error_t *
+svn_rdump__get_dump_editor_v2(svn_editor_t **editor,
+                              svn_revnum_t revision,
+                              svn_stream_t *stream,
+                              svn_ra_session_t *ra_session,
+                              svn_cancel_func_t cancel_func,
+                              void *cancel_baton,
+                              apr_pool_t *scratch_pool,
+                              apr_pool_t *result_pool);
+
 
 /**
  * Load the dumpstream carried in @a stream to the location described

Modified: subversion/branches/issue-4194-dev/subversion/svnserve/cyrus_auth.c
URL: http://svn.apache.org/viewvc/subversion/branches/issue-4194-dev/subversion/svnserve/cyrus_auth.c?rev=1414433&r1=1414432&r2=1414433&view=diff
==============================================================================
--- subversion/branches/issue-4194-dev/subversion/svnserve/cyrus_auth.c (original)
+++ subversion/branches/issue-4194-dev/subversion/svnserve/cyrus_auth.c Tue Nov 27 22:13:24 2012
@@ -98,7 +98,7 @@ static int canonicalize_username(sasl_co
 
 static sasl_callback_t callbacks[] =
 {
-  { SASL_CB_CANON_USER, (void*)canonicalize_username, NULL },
+  { SASL_CB_CANON_USER, (int (*)(void))canonicalize_username, NULL },
   { SASL_CB_LIST_END, NULL, NULL }
 };
 

Modified: subversion/branches/issue-4194-dev/subversion/tests/cmdline/diff_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/issue-4194-dev/subversion/tests/cmdline/diff_tests.py?rev=1414433&r1=1414432&r2=1414433&view=diff
==============================================================================
--- subversion/branches/issue-4194-dev/subversion/tests/cmdline/diff_tests.py (original)
+++ subversion/branches/issue-4194-dev/subversion/tests/cmdline/diff_tests.py Tue Nov 27 22:13:24 2012
@@ -196,8 +196,14 @@ def make_diff_prop_added(pname, pval):
 
 def make_diff_prop_modified(pname, pval1, pval2):
   """Return a property diff for modification of property PNAME, old value
-     PVAL1, new value PVAL2.  PVAL is a single string with no embedded
-     newlines.  Return the result as a list of newline-terminated strings."""
+     PVAL1, new value PVAL2.
+
+     PVAL is a single string with no embedded newlines.  A newline at the
+     end is significant: without it, we add an extra line saying '\ No
+     newline at end of property'.
+
+     Return the result as a list of newline-terminated strings.
+  """
   return [
     "Modified: " + pname + "\n",
     "## -1 +1 ##\n",
@@ -4075,6 +4081,43 @@ def diff_properties_only(sbox):
                                      'diff', '--properties-only',
                                      '-r', 'PREV', 'iota')
 
+def diff_properties_no_newline(sbox):
+  "diff props; check no-newline-at-end messages"
+
+  sbox.build()
+  old_cwd = os.getcwd()
+  os.chdir(sbox.wc_dir)
+  sbox.wc_dir = ''
+
+  no_nl = "\\ No newline at end of property\n"
+  propchange_header = "Modified: p.*\n"
+
+  subtests = [
+    ('p1', 'val1',   'val2'  ),
+    ('p2', 'val1',   'val2\n'),
+    ('p3', 'val1\n', 'val2'  ),
+    ('p4', 'val1\n', 'val2\n'),
+  ]
+
+  # The "before" state.
+  for pname, old_val, new_val in subtests:
+    sbox.simple_propset(pname, old_val, 'iota')
+  sbox.simple_commit() # r2
+
+  # Test one change at a time. (Because, with multiple changes, the order
+  # may not be predictable.)
+  for pname, old_val, new_val in subtests:
+    expected_output = \
+      make_diff_header("iota", "revision 1", "working copy") + \
+      make_diff_prop_header("iota") + \
+      make_diff_prop_modified(pname, old_val, new_val)
+
+    sbox.simple_propset(pname, new_val, 'iota')
+    svntest.actions.run_and_verify_svn(None, expected_output, [], 'diff')
+    svntest.actions.run_and_verify_svn(None, None, [], 'revert', 'iota')
+
+  os.chdir(old_cwd)
+
 ########################################################################
 #Run the tests
 
@@ -4146,6 +4189,7 @@ test_list = [ None,
               diff_deleted_url,
               diff_arbitrary_files_and_dirs,
               diff_properties_only,
+              diff_properties_no_newline,
               ]
 
 if __name__ == '__main__':