Hi,

When running blame on an utf16 text file, the lines can not be used since the blame callback only passes a 'char *' parameter which means it ends at the first zero char. But actually, svn knows if the line has more content. So I'd like to propose the following patch which extends the blame callback with a 'bytelength' parameter indicating how many bytes the line consists of. That way, clients can themselves determine whether to show the full line (e.g. using hex display) or maybe try to convert the line string from different encodings. Without the length parameter, clients can not exceed beyond the first null char to determine what the line really consists of without risking an access violation.

I've already tested this with TSVN and now I can properly blame utf16 files. While it's not straight forward to figure out how to get the real uft16 line, it's possible. One problem is that every line except the first has a zero char at the beginning (the leftover zero char after the detected LF from the previous line), but I can work around that.

So, with this patch I'm able to do a blame on utf16 files, so I'd like to commit this if there are no objections.

[[[
Extend the blame callback with a string length parameter.

* subversion/incluce/svn_client.h
* subversion/libsvn_client/blame.c
  (svn_client_blame_receiver4_t): typedef for new callback
(svn_client_blame6): new API using the svn_client_blame_receiver4_t callback
* subversion/libsvn_client/deprecated.c
  (svn_client_blame5): moved API there, calling svn_client_blame6 using a
                       callback shim
  (blame_wrapper_receiver3): callback shim for svn_client_blame5

]]]
[[[
Extend the blame callback with a string length parameter.

* subversion/incluce/svn_client.h
* subversion/libsvn_client/blame.c
  (svn_client_blame_receiver4_t): typedef for new callback
  (svn_client_blame6): new API using the svn_client_blame_receiver4_t callback
* subversion/libsvn_client/deprecated.c
  (svn_client_blame5): moved API there, calling svn_client_blame6 using a
                       callback shim
  (blame_wrapper_receiver3): callback shim for svn_client_blame5
]]]
Index: subversion/include/svn_client.h
===================================================================
--- subversion/include/svn_client.h     (revision 1850484)
+++ subversion/include/svn_client.h     (working copy)
@@ -736,7 +736,7 @@
  * @{
  */
 
-/** Callback type used by svn_client_blame5() to notify the caller
+/** Callback type used by svn_client_blame6() to notify the caller
  * that line @a line_no of the blamed file was last changed in @a revision
  * which has the revision properties @a rev_props, and that the contents were
  * @a line.
@@ -758,6 +758,29 @@
  * will be true if the reason there is no blame information is that the line
  * was modified locally. In all other cases @a local_change will be false.
  *
+ * @since New in 1.12.
+ */
+typedef svn_error_t *(*svn_client_blame_receiver4_t)(
+  void *baton,
+  svn_revnum_t start_revnum,
+  svn_revnum_t end_revnum,
+  apr_int64_t line_no,
+  svn_revnum_t revision,
+  apr_hash_t *rev_props,
+  svn_revnum_t merged_revision,
+  apr_hash_t *merged_rev_props,
+  const char *merged_path,
+  const char *line,
+  apr_size_t linebytes,
+  svn_boolean_t local_change,
+  apr_pool_t *pool);
+
+/**
+ * Similar to #svn_client_blame_receiver4_t, but without the linebytes
+ * parameter.
+ *
+ * @deprecated Provided for backward compatibility with the 1.11 API.
+ *
  * @since New in 1.7.
  */
 typedef svn_error_t *(*svn_client_blame_receiver3_t)(
@@ -2928,6 +2951,28 @@
  *
  * Use @a pool for any temporary allocation.
  *
+ * @since New in 1.12.
+ */
+svn_error_t *
+svn_client_blame6(const char *path_or_url,
+                  const svn_opt_revision_t *peg_revision,
+                  const svn_opt_revision_t *start,
+                  const svn_opt_revision_t *end,
+                  const svn_diff_file_options_t *diff_options,
+                  svn_boolean_t ignore_mime_type,
+                  svn_boolean_t include_merged_revisions,
+                  svn_client_blame_receiver4_t receiver,
+                  void *receiver_baton,
+                  svn_client_ctx_t *ctx,
+                  apr_pool_t *pool);
+
+
+/**
+ * Similar to svn_client_blame6(), but with #svn_client_blame_receiver3_t
+ * as the receiver.
+ *
+ * @deprecated Provided for backwards compatibility with the 1.6 API.
+ *
  * @since New in 1.7.
  */
 svn_error_t *
@@ -2943,9 +2988,8 @@
                   svn_client_ctx_t *ctx,
                   apr_pool_t *pool);
 
-
 /**
- * Similar to svn_client_blame5(), but with #svn_client_blame_receiver3_t
+ * Similar to svn_client_blame5(), but with #svn_client_blame_receiver2_t
  * as the receiver.
  *
  * @deprecated Provided for backwards compatibility with the 1.6 API.
Index: subversion/libsvn_client/blame.c
===================================================================
--- subversion/libsvn_client/blame.c    (revision 1850484)
+++ subversion/libsvn_client/blame.c    (working copy)
@@ -656,7 +656,7 @@
 }
 
 svn_error_t *
-svn_client_blame5(const char *target,
+svn_client_blame6(const char *target,
                   const svn_opt_revision_t *peg_revision,
                   const svn_opt_revision_t *start,
                   const svn_opt_revision_t *end,
@@ -663,7 +663,7 @@
                   const svn_diff_file_options_t *diff_options,
                   svn_boolean_t ignore_mime_type,
                   svn_boolean_t include_merged_revisions,
-                  svn_client_blame_receiver3_t receiver,
+                  svn_client_blame_receiver4_t receiver,
                   void *receiver_baton,
                   svn_client_ctx_t *ctx,
                   apr_pool_t *pool)
@@ -946,13 +946,13 @@
                                  line_no, walk->rev->revision,
                                  walk->rev->rev_props, merged_rev,
                                  merged_rev_props, merged_path,
-                                 sb->data, FALSE, iterpool));
+                                 sb->data, sb->len, FALSE, iterpool));
               else
                 SVN_ERR(receiver(receiver_baton, start_revnum, end_revnum,
                                  line_no, SVN_INVALID_REVNUM,
                                  NULL, SVN_INVALID_REVNUM,
                                  NULL, NULL,
-                                 sb->data, TRUE, iterpool));
+                                 sb->data, sb->len, TRUE, iterpool));
             }
           if (eof) break;
         }
Index: subversion/libsvn_client/deprecated.c
===================================================================
--- subversion/libsvn_client/deprecated.c       (revision 1850484)
+++ subversion/libsvn_client/deprecated.c       (working copy)
@@ -166,7 +166,60 @@
 }
 
 /*** From blame.c ***/
+struct blame_receiver_wrapper_baton3 {
+  void *baton;
+  svn_client_blame_receiver3_t receiver;
+};
 
+static svn_error_t *
+blame_wrapper_receiver3(void *baton,
+   svn_revnum_t start_revnum,
+   svn_revnum_t end_revnum,
+   apr_int64_t line_no,
+   svn_revnum_t revision,
+   apr_hash_t *rev_props,
+   svn_revnum_t merged_revision,
+   apr_hash_t *merged_rev_props,
+   const char *merged_path,
+   const char *line,
+   apr_size_t linebytes,
+   svn_boolean_t local_change,
+   apr_pool_t *pool)
+{
+  struct blame_receiver_wrapper_baton3 *brwb = baton;
+
+  if (brwb->receiver)
+    return brwb->receiver(brwb->baton, start_revnum, end_revnum, line_no,
+                          revision, rev_props, merged_revision,
+                          merged_rev_props, merged_path, line,
+                          local_change, pool);
+
+  return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_client_blame5(const char *target,
+                  const svn_opt_revision_t *peg_revision,
+                  const svn_opt_revision_t *start,
+                  const svn_opt_revision_t *end,
+                  const svn_diff_file_options_t *diff_options,
+                  svn_boolean_t ignore_mime_type,
+                  svn_boolean_t include_merged_revisions,
+                  svn_client_blame_receiver3_t receiver,
+                  void *receiver_baton,
+                  svn_client_ctx_t *ctx,
+                  apr_pool_t *pool)
+{
+  struct blame_receiver_wrapper_baton3 baton;
+
+  baton.receiver = receiver;
+  baton.baton = receiver_baton;
+
+  return svn_client_blame6(target, peg_revision, start, end, diff_options,
+                           ignore_mime_type, include_merged_revisions,
+                           blame_wrapper_receiver3, &baton, ctx, pool);
+}
+
 struct blame_receiver_wrapper_baton2 {
   void *baton;
   svn_client_blame_receiver2_t receiver;

Reply via email to