Author: brane
Date: Fri Nov 23 16:27:50 2012
New Revision: 1412942
URL: http://svn.apache.org/viewvc?rev=1412942&view=rev
Log:
Issue #4261 (require --force to set unknown svn: prop names)
* subversion/svn/cl.h (svn_cl__check_svn_prop_name): New prototype.
* subversion/svn/props.c: Include stdlib.h for qsort or qsort_r.
Include svn_string_private.h for svn_string__similarity.
(svn_cl__check_svn_prop_name): Implement.
(simprop_context_t, simprop_t, simprop_key_diff, simprop_compare):
New helpers for svn_cl__check_svn_prop_name.
Modified:
subversion/trunk/subversion/svn/cl.h
subversion/trunk/subversion/svn/props.c
Modified: subversion/trunk/subversion/svn/cl.h
URL:
http://svn.apache.org/viewvc/subversion/trunk/subversion/svn/cl.h?rev=1412942&r1=1412941&r2=1412942&view=diff
==============================================================================
--- subversion/trunk/subversion/svn/cl.h (original)
+++ subversion/trunk/subversion/svn/cl.h Fri Nov 23 16:27:50 2012
@@ -767,6 +767,17 @@ 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.
+ *
+ * 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/trunk/subversion/svn/props.c
URL:
http://svn.apache.org/viewvc/subversion/trunk/subversion/svn/props.c?rev=1412942&r1=1412941&r2=1412942&view=diff
==============================================================================
--- subversion/trunk/subversion/svn/props.c (original)
+++ subversion/trunk/subversion/svn/props.c Fri Nov 23 16:27:50 2012
@@ -27,6 +27,8 @@
/*** Includes. ***/
+#include <stdlib.h>
+
#include <apr_hash.h>
#include "svn_cmdline.h"
#include "svn_string.h"
@@ -40,6 +42,7 @@
#include "svn_base64.h"
#include "cl.h"
+#include "private/svn_string_private.h"
#include "private/svn_cmdline_private.h"
#include "svn_private_config.h"
@@ -223,3 +226,152 @@ 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 checking against */
+ svn_membuf_t buffer; /* Buffer for similariry testing */
+};
+
+struct simprop_t
+{
+ svn_string_t name; /* svn: property name */
+ unsigned int score; /* the similarity score */
+ apr_size_t diff; /* number of chars different from context.name */
+#if !HAVE_QSORT_R
+ struct simprop_context_t *context; /* sorting context for qsort() */
+#endif
+};
+
+/* 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 or qsort_r for simprop_t */
+#if !HAVE_QSORT_R
+static int
+simprop_compare(const void *pkeya, const void *pkeyb)
+#else
+static int
+simprop_compare(void *pcontext, const void *pkeya, const void *pkeyb)
+#endif
+{
+ struct simprop_t *const keya = *(struct simprop_t *const *)pkeya;
+ struct simprop_t *const keyb = *(struct simprop_t *const *)pkeyb;
+#if !HAVE_QSORT_R
+ struct simprop_context_t *const context = keya->context;
+#else
+ struct simprop_context_t *const context = pcontext;
+#endif
+
+ 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 char *const revprops[] =
+ {
+ SVN_PROP_REVISION_ALL_PROPS
+ };
+
+ struct simprop_t **propkeys;
+ struct simprop_t *propbuf;
+ const char *const *proplist;
+ apr_size_t numprops;
+ 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 */
+ }
+
+ /* Now find the closest match from amongst a the set of reserved
+ node or revision property names. */
+ if (revprop)
+ {
+ proplist = revprops;
+ numprops = sizeof(revprops) / sizeof(*revprops);
+ }
+ else
+ {
+ proplist = nodeprops;
+ numprops = sizeof(nodeprops) / sizeof(*nodeprops);
+ }
+
+ propkeys = apr_palloc(scratch_pool,
+ numprops * sizeof(struct simprop_t*));
+ propbuf = apr_palloc(scratch_pool,
+ numprops * sizeof(struct simprop_t));
+ for (i = 0; i < numprops; ++i)
+ {
+ propkeys[i] = &propbuf[i];
+ propbuf[i].name.data = proplist[i];
+ propbuf[i].name.len = strlen(proplist[i]);
+ propbuf[i].score = -1;
+#if !HAVE_QSORT_R
+ propbuf[i].context = &context;
+#endif
+ }
+
+ SVN_QSORT_R(propkeys, numprops, sizeof(*propkeys),
+ simprop_compare, &context);
+
+ if (0 == propkeys[0]->diff)
+ return SVN_NO_ERROR; /* We found an exact match. */
+
+ /* ### suggest a list of the most likely candidates instead? */
+ 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]->name.data, propname);
+}