On 13/05/13 22:53, Gabriela Gibson wrote:
Hi,

thanks for all the comments and help, here is the next attempt.

Gabriela


Sorry I had one mistake in the patch that has now been fixed.
Please ignore the patch in the previous post, the attached patch is the working one.

Gabriela


Index: subversion/include/svn_client.h
===================================================================
--- subversion/include/svn_client.h	(revision 1480974)
+++ subversion/include/svn_client.h	(working copy)
@@ -2986,6 +2986,11 @@ svn_client_blame(const char *path_or_url,
  * The above two options are mutually exclusive. It is an error to set
  * both to TRUE.
  *
+ * @a invoke_diff_cmd is used to call an external diff program but may
+ * not be @c NULL.  The command line invocation will override the
+ * invoke-diff-cmd invocation entry(if any) in the Subversion
+ * configuration file.
+ *
  * Generated headers are encoded using @a header_encoding.
  *
  * Diff output will not be generated for binary files, unless @a
@@ -3016,8 +3021,38 @@ svn_client_blame(const char *path_or_url,
  * @note @a relative_to_dir doesn't affect the path index generated by
  * external diff programs.
  *
+ * @since New in 1.9.
+ */
+svn_error_t *
+svn_client_diff7(const apr_array_header_t *options,
+                 const char *path_or_url1,
+                 const svn_opt_revision_t *revision1,
+                 const char *path_or_url2,
+                 const svn_opt_revision_t *revision2,
+                 const char *relative_to_dir,
+                 svn_depth_t depth,
+                 svn_boolean_t ignore_ancestry,
+                 svn_boolean_t no_diff_added,
+                 svn_boolean_t no_diff_deleted,
+                 svn_boolean_t show_copies_as_adds,
+                 svn_boolean_t ignore_content_type,
+                 svn_boolean_t ignore_properties,
+                 svn_boolean_t properties_only,
+                 svn_boolean_t use_git_diff_format,
+                 const char *header_encoding,
+                 svn_stream_t *outstream,
+                 svn_stream_t *errstream,
+                 const apr_array_header_t *changelists,
+                 const char *invoke_diff_cmd,
+                 svn_client_ctx_t *ctx,
+                 apr_pool_t *pool);
+
+/** Similar to svn_client_diff7(), but with @a invoke_diff_cmd.
+ *
+ * @deprecated Provided for backward compatibility with the 1.8 API.
  * @since New in 1.8.
  */
+SVN_DEPRECATED
 svn_error_t *
 svn_client_diff6(const apr_array_header_t *diff_options,
                  const char *path_or_url1,
@@ -3175,14 +3210,47 @@ svn_client_diff(const apr_array_header_t *diff_opt
  * be either a working-copy path or URL.
  *
  * If @a peg_revision is #svn_opt_revision_unspecified, behave
- * identically to svn_client_diff6(), using @a path_or_url for both of that
+ * identically to svn_client_diff7(), using @a path_or_url for both of that 
  * function's @a path_or_url1 and @a path_or_url2 arguments.
  *
- * All other options are handled identically to svn_client_diff6().
+ * All other options are handled identically to svn_client_diff7().
  *
- * @since New in 1.8.
+ * @since New in 1.9.
  */
 svn_error_t *
+svn_client_diff_peg7(const apr_array_header_t *diff_options,
+                     const char *path_or_url,
+                     const svn_opt_revision_t *peg_revision,
+                     const svn_opt_revision_t *start_revision,
+                     const svn_opt_revision_t *end_revision,
+                     const char *relative_to_dir,
+                     svn_depth_t depth,
+                     svn_boolean_t ignore_ancestry,
+                     svn_boolean_t no_diff_added,
+                     svn_boolean_t no_diff_deleted,
+                     svn_boolean_t show_copies_as_adds,
+                     svn_boolean_t ignore_content_type,
+                     svn_boolean_t ignore_properties,
+                     svn_boolean_t properties_only,
+                     svn_boolean_t use_git_diff_format,
+                     const char *header_encoding,
+                     svn_stream_t *outstream,
+                     svn_stream_t *errstream,
+                     const apr_array_header_t *changelists,
+                     const char *invoke_diff_cmd,
+                     svn_client_ctx_t *ctx,
+                     apr_pool_t *pool);
+         
+
+/** Similar to svn_client_peg5(), but with @a no_diff_added set to
+ *  FALSE, @a ignore_properties set to FALSE and @a properties_only
+ *  set to false.
+ *
+ * @deprecated Provided for backward compatibility with the 1.7 API.
+ * @since New in 1.9.
+ */
+SVN_DEPRECATED
+svn_error_t *
 svn_client_diff_peg6(const apr_array_header_t *diff_options,
                      const char *path_or_url,
                      const svn_opt_revision_t *peg_revision,
Index: subversion/include/svn_config.h
===================================================================
--- subversion/include/svn_config.h	(revision 1480974)
+++ subversion/include/svn_config.h	(working copy)
@@ -112,6 +112,8 @@ typedef struct svn_config_t svn_config_t;
 #define SVN_CONFIG_OPTION_DIFF_EXTENSIONS           "diff-extensions"
 #define SVN_CONFIG_OPTION_DIFF3_CMD                 "diff3-cmd"
 #define SVN_CONFIG_OPTION_DIFF3_HAS_PROGRAM_ARG     "diff3-has-program-arg"
+/** @since New in 1.9. */
+#define SVN_CONFIG_OPTION_INVOKE_DIFF_CMD           "invoke-diff-cmd"
 #define SVN_CONFIG_OPTION_MERGE_TOOL_CMD            "merge-tool-cmd"
 #define SVN_CONFIG_SECTION_MISCELLANY           "miscellany"
 #define SVN_CONFIG_OPTION_GLOBAL_IGNORES            "global-ignores"
Index: subversion/include/svn_io.h
===================================================================
--- subversion/include/svn_io.h	(revision 1480974)
+++ subversion/include/svn_io.h	(working copy)
@@ -2278,6 +2278,38 @@ svn_io_file_readline(apr_file_t *file,
 
 /** @} */
 
+/** Parse a user defined command to contain dynamically created labels
+ *  and filenames.
+ *  
+ * @since New in 1.9.
+ */
+const char **
+svn_io_create_custom_diff_cmd(const char *label1,
+                              const char *label2,
+                              const char *label3,
+                              const char *tmpfile1,
+                              const char *tmpfile2,
+                              const char *base,
+                              const char *cmd,
+                              apr_pool_t *scratch_pool);
+
+/** Run the external diff command defined by the invoke-diff-cmd
+ *  option.
+ *  
+ *  @since New in 1.9.
+ */
+svn_error_t *
+svn_io_run_external_diff(const char *dir,
+                         const char *label1,
+                         const char *label2,
+                         const char *tmpfile1,
+                         const char *tmpfile2,
+                         int *pexitcode,
+                         apr_file_t *outfile,
+                         apr_file_t *errfile,
+                         const char *external_diff_cmd,
+                         apr_pool_t *scratch_pool);
+
 #ifdef __cplusplus
 }
 #endif /* __cplusplus */
Index: subversion/libsvn_client/deprecated.c
===================================================================
--- subversion/libsvn_client/deprecated.c	(revision 1480974)
+++ subversion/libsvn_client/deprecated.c	(working copy)
@@ -914,6 +914,53 @@ svn_client_delete(svn_client_commit_info_t **commi
 /*** From diff.c ***/
 
 svn_error_t *
+svn_client_diff6(const apr_array_header_t *options,
+                 const char *path_or_url1,
+                 const svn_opt_revision_t *revision1,
+                 const char *path_or_url2,
+                 const svn_opt_revision_t *revision2,
+                 const char *relative_to_dir,
+                 svn_depth_t depth,
+                 svn_boolean_t ignore_ancestry,
+                 svn_boolean_t no_diff_added,
+                 svn_boolean_t no_diff_deleted,
+                 svn_boolean_t show_copies_as_adds,
+                 svn_boolean_t ignore_content_type,
+                 svn_boolean_t ignore_properties,
+                 svn_boolean_t properties_only,
+                 svn_boolean_t use_git_diff_format,
+                 const char *header_encoding,
+                 svn_stream_t *outstream,
+                 svn_stream_t *errstream,
+                 const apr_array_header_t *changelists,
+                 svn_client_ctx_t *ctx,
+                 apr_pool_t *pool)
+{
+  return svn_client_diff7(options,
+                          path_or_url1,
+                          revision1,
+                          path_or_url2,
+                          revision2,
+                          relative_to_dir,
+                          depth,
+                          ignore_ancestry,
+                          no_diff_added,
+                          no_diff_deleted,
+                          show_copies_as_adds,
+                          ignore_content_type,
+                          ignore_properties,
+                          properties_only,
+                          use_git_diff_format,
+                          header_encoding,
+                          outstream,
+                          errstream,
+                          changelists,
+                          NULL,
+                          ctx, 
+                          pool);
+}
+
+svn_error_t *
 svn_client_diff5(const apr_array_header_t *diff_options,
                  const char *path1,
                  const svn_opt_revision_t *revision1,
@@ -1036,6 +1083,53 @@ svn_client_diff(const apr_array_header_t *options,
 }
 
 svn_error_t *
+svn_client_diff_peg6(const apr_array_header_t *options,
+                     const char *path_or_url,
+                     const svn_opt_revision_t *peg_revision,
+                     const svn_opt_revision_t *start_revision,
+                     const svn_opt_revision_t *end_revision,
+                     const char *relative_to_dir,
+                     svn_depth_t depth,
+                     svn_boolean_t ignore_ancestry,
+                     svn_boolean_t no_diff_added,
+                     svn_boolean_t no_diff_deleted,
+                     svn_boolean_t show_copies_as_adds,
+                     svn_boolean_t ignore_content_type,
+                     svn_boolean_t ignore_properties,
+                     svn_boolean_t properties_only,
+                     svn_boolean_t use_git_diff_format,
+                     const char *header_encoding,
+                     svn_stream_t *outstream,
+                     svn_stream_t *errstream,
+                     const apr_array_header_t *changelists,
+                     svn_client_ctx_t *ctx,
+                     apr_pool_t *pool)
+{
+  return svn_client_diff_peg7(options,
+                              path_or_url,
+                              peg_revision,
+                              start_revision,
+                              end_revision,
+                              relative_to_dir,
+                              depth,
+                              ignore_ancestry,
+                              no_diff_added,
+                              no_diff_deleted,
+                              show_copies_as_adds,
+                              ignore_content_type,
+                              ignore_properties,
+                              properties_only,
+                              use_git_diff_format,
+                              header_encoding,
+                              outstream,
+                              errstream,
+                              changelists,
+                              NULL,
+                              ctx,
+                              pool);
+}
+
+svn_error_t *
 svn_client_diff_peg5(const apr_array_header_t *diff_options,
                      const char *path,
                      const svn_opt_revision_t *peg_revision,
Index: subversion/libsvn_client/diff.c
===================================================================
--- subversion/libsvn_client/diff.c	(revision 1480974)
+++ subversion/libsvn_client/diff.c	(working copy)
@@ -427,7 +427,7 @@ print_git_diff_header(svn_stream_t *os,
 
 /* A helper func that writes out verbal descriptions of property diffs
    to OUTSTREAM.   Of course, OUTSTREAM will probably be whatever was
-   passed to svn_client_diff6(), which is probably stdout.
+   passed to svn_client_diff7(), which is probably stdout.
 
    ### FIXME needs proper docstring
 
@@ -563,7 +563,7 @@ struct diff_cmd_baton {
   const char *orig_path_2;
 
   /* These are the numeric representations of the revisions passed to
-     svn_client_diff6(), either may be SVN_INVALID_REVNUM.  We need these
+     svn_client_diff7(), either may be SVN_INVALID_REVNUM.  We need these
      because some of the svn_wc_diff_callbacks4_t don't get revision
      arguments.
 
@@ -611,6 +611,10 @@ struct diff_cmd_baton {
 
   /* Whether the local diff target of a repos->wc diff is a copy. */
   svn_boolean_t repos_wc_diff_target_is_copy;
+
+  /* external custom diff command */
+  const char *invoke_diff_cmd;
+
 };
 
 /* An helper for diff_dir_props_changed, diff_file_changed and diff_file_added
@@ -786,7 +790,7 @@ diff_content_changed(svn_boolean_t *wrote_header,
     }
 
 
-  if (diff_cmd_baton->diff_cmd)
+  if (diff_cmd_baton->diff_cmd || diff_cmd_baton->invoke_diff_cmd)
     {
       apr_file_t *outfile;
       apr_file_t *errfile;
@@ -815,15 +819,25 @@ diff_content_changed(svn_boolean_t *wrote_header,
       SVN_ERR(svn_io_open_unique_file3(&errfile, &errfilename, NULL,
                                        svn_io_file_del_on_pool_cleanup,
                                        scratch_pool, scratch_pool));
-
-      SVN_ERR(svn_io_run_diff2(".",
-                               diff_cmd_baton->options.for_external.argv,
-                               diff_cmd_baton->options.for_external.argc,
-                               label1, label2,
-                               tmpfile1, tmpfile2,
-                               &exitcode, outfile, errfile,
-                               diff_cmd_baton->diff_cmd, scratch_pool));
-
+      
+      if (diff_cmd_baton->diff_cmd) 
+        SVN_ERR(svn_io_run_diff2(".",
+                                 diff_cmd_baton->options.for_external.argv,
+                                 diff_cmd_baton->options.for_external.argc,
+                                 label1, label2,
+                                 tmpfile1, tmpfile2,
+                                 &exitcode, outfile, errfile,
+                                 diff_cmd_baton->diff_cmd, scratch_pool));
+      else
+        { 
+          SVN_ERR(
+          svn_io_run_external_diff(".",
+                                   label1, label2,
+                                   tmpfile1, tmpfile2,
+                                   &exitcode, outfile, errfile,
+                                   diff_cmd_baton->invoke_diff_cmd,
+                                   scratch_pool));
+        }  
       SVN_ERR(svn_io_file_close(outfile, scratch_pool));
       SVN_ERR(svn_io_file_close(errfile, scratch_pool));
 
@@ -1519,8 +1533,8 @@ diff_prepare_repos_repos(const char **url1,
 
 /* A Theoretical Note From Ben, regarding do_diff().
 
-   This function is really svn_client_diff6().  If you read the public
-   API description for svn_client_diff6(), it sounds quite Grand.  It
+   This function is really svn_client_diff7().  If you read the public
+   API description for svn_client_diff7(), it sounds quite Grand.  It
    sounds really generalized and abstract and beautiful: that it will
    diff any two paths, be they working-copy paths or URLs, at any two
    revisions.
@@ -1544,7 +1558,7 @@ diff_prepare_repos_repos(const char **url1,
    pigeonholed into one of these use-cases, we currently bail with a
    friendly apology.
 
-   Perhaps someday a brave soul will truly make svn_client_diff6()
+   Perhaps someday a brave soul will truly make svn_client_diff7()
    perfectly general.  For now, we live with the 90% case.  Certainly,
    the commandline client only calls this function in legal ways.
    When there are other users of svn_client.h, maybe this will become
@@ -1557,7 +1571,7 @@ static svn_error_t *
 unsupported_diff_error(svn_error_t *child_err)
 {
   return svn_error_create(SVN_ERR_INCORRECT_PARAMS, child_err,
-                          _("Sorry, svn_client_diff6 was called in a way "
+                          _("Sorry, svn_client_diff7 was called in a way "
                             "that is not yet supported"));
 }
 
@@ -1566,7 +1580,7 @@ unsupported_diff_error(svn_error_t *child_err)
    PATH1 and PATH2 are both working copy paths.  REVISION1 and
    REVISION2 are their respective revisions.
 
-   All other options are the same as those passed to svn_client_diff6(). */
+   All other options are the same as those passed to svn_client_diff7(). */
 static svn_error_t *
 diff_wc_wc(const char *path1,
            const svn_opt_revision_t *revision1,
@@ -1649,7 +1663,7 @@ diff_wc_wc(const char *path1,
    and the actual two paths compared are determined by following copy
    history from PATH_OR_URL2.
 
-   All other options are the same as those passed to svn_client_diff6(). */
+   All other options are the same as those passed to svn_client_diff7(). */
 static svn_error_t *
 diff_repos_repos(const svn_wc_diff_callbacks4_t *callbacks,
                  struct diff_cmd_baton *callback_baton,
@@ -1794,7 +1808,7 @@ diff_repos_repos(const svn_wc_diff_callbacks4_t *c
    revision, and the actual repository path to be compared is
    determined by following copy history.
 
-   All other options are the same as those passed to svn_client_diff6(). */
+   All other options are the same as those passed to svn_client_diff7(). */
 static svn_error_t *
 diff_repos_wc(const char *path_or_url1,
               const svn_opt_revision_t *revision1,
@@ -2129,7 +2143,7 @@ do_diff(const svn_wc_diff_callbacks4_t *callbacks,
    revision, and the actual repository path to be compared is
    determined by following copy history.
 
-   All other options are the same as those passed to svn_client_diff6(). */
+   All other options are the same as those passed to svn_client_diff7(). */
 static svn_error_t *
 diff_summarize_repos_wc(svn_client_diff_summarize_func_t summarize_func,
                         void *summarize_baton,
@@ -2173,7 +2187,7 @@ diff_summarize_repos_wc(svn_client_diff_summarize_
    PATH1 and PATH2 are both working copy paths.  REVISION1 and
    REVISION2 are their respective revisions.
 
-   All other options are the same as those passed to svn_client_diff6(). */
+   All other options are the same as those passed to svn_client_diff7(). */
 static svn_error_t *
 diff_summarize_wc_wc(svn_client_diff_summarize_func_t summarize_func,
                      void *summarize_baton,
@@ -2448,7 +2462,7 @@ set_up_diff_cmd_and_options(struct diff_cmd_baton
 {
   const char *diff_cmd = NULL;
 
-  /* See if there is a diff command and/or diff arguments. */
+  /* old style diff_cmd has precedence in config file */
   if (config)
     {
       svn_config_t *cfg = svn_hash_gets(config, SVN_CONFIG_CATEGORY_CONFIG);
@@ -2455,15 +2469,14 @@ set_up_diff_cmd_and_options(struct diff_cmd_baton
       svn_config_get(cfg, &diff_cmd, SVN_CONFIG_SECTION_HELPERS,
                      SVN_CONFIG_OPTION_DIFF_CMD, NULL);
       if (options == NULL)
-        {
-          const char *diff_extensions;
-          svn_config_get(cfg, &diff_extensions, SVN_CONFIG_SECTION_HELPERS,
-                         SVN_CONFIG_OPTION_DIFF_EXTENSIONS, NULL);
-          if (diff_extensions)
-            options = svn_cstring_split(diff_extensions, " \t\n\r", TRUE, pool);
-        }
+      {
+        const char *diff_extensions;
+        svn_config_get(cfg, &diff_extensions, SVN_CONFIG_SECTION_HELPERS,
+                       SVN_CONFIG_OPTION_DIFF_EXTENSIONS, NULL);
+        if (diff_extensions)
+          options = svn_cstring_split(diff_extensions, " \t\n\r", TRUE, pool);
+      }
     }
-
   if (options == NULL)
     options = apr_array_make(pool, 0, sizeof(const char *));
 
@@ -2470,8 +2483,22 @@ set_up_diff_cmd_and_options(struct diff_cmd_baton
   if (diff_cmd)
     SVN_ERR(svn_path_cstring_to_utf8(&diff_cmd_baton->diff_cmd, diff_cmd,
                                      pool));
-  else
-    diff_cmd_baton->diff_cmd = NULL;
+  else {
+    if (config) /* check if there is a invoke_diff_cmd in the config file */
+     {
+       svn_config_t *cfg = svn_hash_gets(config, SVN_CONFIG_CATEGORY_CONFIG);
+       diff_cmd_baton->diff_cmd = NULL; 
+       svn_config_get(cfg, &diff_cmd, SVN_CONFIG_SECTION_HELPERS,
+                      SVN_CONFIG_OPTION_INVOKE_DIFF_CMD, NULL);
+       if (diff_cmd) 
+       {
+         SVN_ERR(svn_path_cstring_to_utf8(
+                   &diff_cmd_baton->invoke_diff_cmd, diff_cmd, pool));
+         
+      return SVN_NO_ERROR;
+       }
+     }
+   }
 
   /* If there was a command, arrange options to pass to it. */
   if (diff_cmd_baton->diff_cmd)
@@ -2537,7 +2564,7 @@ set_up_diff_cmd_and_options(struct diff_cmd_baton
       * These cases require server communication.
 */
 svn_error_t *
-svn_client_diff6(const apr_array_header_t *options,
+svn_client_diff7(const apr_array_header_t *options,
                  const char *path_or_url1,
                  const svn_opt_revision_t *revision1,
                  const char *path_or_url2,
@@ -2556,6 +2583,7 @@ svn_error_t *
                  svn_stream_t *outstream,
                  svn_stream_t *errstream,
                  const apr_array_header_t *changelists,
+                 const char *invoke_diff_cmd,
                  svn_client_ctx_t *ctx,
                  apr_pool_t *pool)
 {
@@ -2573,7 +2601,8 @@ svn_error_t *
   /* setup callback and baton */
   diff_cmd_baton.orig_path_1 = path_or_url1;
   diff_cmd_baton.orig_path_2 = path_or_url2;
-
+  diff_cmd_baton.invoke_diff_cmd = invoke_diff_cmd;
+  
   SVN_ERR(set_up_diff_cmd_and_options(&diff_cmd_baton, options,
                                       ctx->config, pool));
   diff_cmd_baton.pool = pool;
@@ -2604,7 +2633,7 @@ svn_error_t *
 }
 
 svn_error_t *
-svn_client_diff_peg6(const apr_array_header_t *options,
+svn_client_diff_peg7(const apr_array_header_t *options,
                      const char *path_or_url,
                      const svn_opt_revision_t *peg_revision,
                      const svn_opt_revision_t *start_revision,
@@ -2623,6 +2652,7 @@ svn_error_t *
                      svn_stream_t *outstream,
                      svn_stream_t *errstream,
                      const apr_array_header_t *changelists,
+                     const char *invoke_diff_cmd,
                      svn_client_ctx_t *ctx,
                      apr_pool_t *pool)
 {
@@ -2636,6 +2666,7 @@ svn_error_t *
   /* setup callback and baton */
   diff_cmd_baton.orig_path_1 = path_or_url;
   diff_cmd_baton.orig_path_2 = path_or_url;
+  diff_cmd_baton.invoke_diff_cmd = invoke_diff_cmd;
 
   SVN_ERR(set_up_diff_cmd_and_options(&diff_cmd_baton, options,
                                       ctx->config, pool));
Index: subversion/libsvn_subr/config_file.c
===================================================================
--- subversion/libsvn_subr/config_file.c	(revision 1480974)
+++ subversion/libsvn_subr/config_file.c	(working copy)
@@ -1084,6 +1084,11 @@ svn_config_ensure(const char *config_dir, apr_pool
         "### Set diff3-has-program-arg to 'yes' if your 'diff3' program"     NL
         "###   accepts the '--diff-program' option."                         NL
         "# diff3-has-program-arg = [yes | no]"                               NL
+        "### Set invoke-diff-cmd to the absolute path of your 'diff'"        NL
+        "### program."                                                       NL
+        "###   This will override the compile-time default, which is to use" NL
+        "###   Subversion's internal diff implementation."                   NL
+        "# invoke-diff-cmd = \"diff -y --label %l1% %f1% --label %l2% %f2%\""NL
         "### Set merge-tool-cmd to the command used to invoke your external" NL
         "### merging tool of choice. Subversion will pass 5 arguments to"    NL
         "### the specified command: base theirs mine merged wcfile"          NL
Index: subversion/libsvn_subr/io.c
===================================================================
--- subversion/libsvn_subr/io.c	(revision 1480974)
+++ subversion/libsvn_subr/io.c	(working copy)
@@ -2921,8 +2921,153 @@ svn_io_run_diff2(const char *dir,
   return SVN_NO_ERROR;
 }
 
+const char **
+svn_io_create_custom_diff_cmd(const char *label1,
+                              const char *label2,
+                              const char *label3,
+                              const char *tmpfile1,
+                              const char *tmpfile2,
+                              const char *base,
+                              const char *cmd,
+                              apr_pool_t *pool)
+{
+  apr_pool_t *subpool; 
+  static const char *token_list[] = 
+    {"%f1%","%f2%", "%f3%", "%l1%", "%l2%","%l3%" };
+  const char *delimiters[] = {"#","^","&","!","~","?","$","*","|"}; 
+  svn_stringbuf_t *com;
+  apr_array_header_t *tmp;
+  const char ** ret;
+  int i, delimiter;
+  char * found;
 
+  subpool = svn_pool_create(pool);
+  delimiter = 0;
+  
+  /* find a delimiter that does not occur in the user string. */
+  while ( ((found = strstr(cmd, delimiters[delimiter++]))) && 
+          (delimiter < 9 /* sizeof(delimiters) */ ) )
+  { ; } delimiter--;
+  
+  tmp = svn_cstring_split(cmd," ",TRUE, subpool);
+  com = svn_stringbuf_create_empty(subpool);
+
+  /* ensure that the command is split into the correct argv parcels
+   * and leave a marker in place because the correct order is lost in
+   * the subsequent in-place replacements due to labels having
+   * spaces and ending up as 2 entries in ret[] */ 
+  for (i = 0; i < tmp->nelts ; i++)
+  {
+    svn_stringbuf_appendcstr(com, APR_ARRAY_IDX(tmp, i, char *));
+    svn_stringbuf_appendcstr(com, delimiters[delimiter]);  
+  }
+
+  for (i = 0; i < 6 /* sizeof(token_list) */; i++) 
+    {
+      svn_stringbuf_t *token;
+      int len;
+
+      token = svn_stringbuf_create_empty(subpool);
+      svn_stringbuf_appendcstr(token, token_list[i]);
+      len = 0;
+      while ( (found = strstr(com->data, token->data)) && 
+              (strlen(found) > len) ) 
+        {
+          len = strlen(found); 
+
+          /* if we find a % in front of this, consume it */
+          if (com->data[com->len - strlen(found)-1] == '%')
+            svn_stringbuf_remove(com, strlen(found)-1, 1);
+          else if (i == 0) /* %f1 */
+            svn_stringbuf_replace(com, com->len - strlen(found), token->len,
+                                  tmpfile1, strlen(tmpfile1));
+          else if (i == 1) /* %f2 */
+            svn_stringbuf_replace(com, com->len - strlen(found), token->len, 
+                                  tmpfile2, strlen(tmpfile2));
+          else if (i == 2) /* %f3 */
+            svn_stringbuf_replace(com, com->len - strlen(found), token->len, 
+                                  base, strlen(base));
+          else if (i == 3) /* %l1 */
+            svn_stringbuf_replace(com, com->len - strlen(found), token->len, 
+                                  label1, strlen(label1));
+          else if (i == 4) /* %l2 */
+            svn_stringbuf_replace(com, com->len - strlen(found), token->len, 
+                                  label2, strlen(label2));
+          else if (i == 5) /* %l3 */
+            svn_stringbuf_replace(com, com->len - strlen(found), token->len, 
+                                  label3, strlen(label3));
+        }
+    }
+  
+  /* Produce a null-terminated argv[] required by apr_proc_create() */
+  tmp = svn_cstring_split(com->data,delimiters[delimiter],TRUE, pool);
+      
+  ret = apr_pcalloc(pool, 
+                    tmp->nelts * 
+                    tmp->elt_size*sizeof(char *));  
+      
+  for (i = 0; i < tmp->nelts ; i++)
+    ret[i] = APR_ARRAY_IDX(tmp, i, char *);
+  ret[i] = NULL;
+
+  svn_pool_destroy(subpool);
+  return ret;
+}
+
 svn_error_t *
+svn_io_run_external_diff(const char *dir,
+                         const char *label1,
+                         const char *label2,
+                         const char *tmpfile1,
+                         const char *tmpfile2,
+                         int *pexitcode,
+                         apr_file_t *outfile,
+                         apr_file_t *errfile,
+                         const char *external_diff_cmd,
+                         apr_pool_t *pool)
+{
+  int exitcode;
+  const char ** cmd;
+
+  if (0 == strlen(external_diff_cmd)) 
+     return svn_error_createf(SVN_ERR_CL_INSUFFICIENT_ARGS, NULL,
+                        _("The --invoke-diff-cmd string was empty.\n"));
+
+  cmd = svn_io_create_custom_diff_cmd(label1, label2, NULL, 
+                                      tmpfile1, tmpfile2, NULL, 
+                                      external_diff_cmd, pool);
+  if (pexitcode == NULL)
+     pexitcode = &exitcode;
+  
+  SVN_ERR(svn_io_run_cmd(dir, cmd[0], cmd, pexitcode, NULL, TRUE,
+                         NULL, outfile, errfile, pool));
+  
+  if (*pexitcode != 0 && *pexitcode != 1)
+   {
+       int i, size;
+       char * failed_command;
+
+       for (i = 0, size = 0; cmd[i]; i++) 
+         size += strlen(cmd[i]) + 1;
+
+       failed_command = apr_pcalloc(pool, size * sizeof(char *));
+
+       for (i = 0; cmd[i]; i++) 
+        {
+         strcat(failed_command, cmd[i]);
+         strcat(failed_command, " ");
+        }
+       
+       return svn_error_createf(SVN_ERR_EXTERNAL_PROGRAM, NULL,
+                                _("'%s' was expanded to '%s' and returned %d\n"),
+                                external_diff_cmd,
+                                svn_dirent_local_style(failed_command, pool),
+                                *pexitcode);
+   }
+  return SVN_NO_ERROR;
+}
+
+svn_error_t *
 svn_io_run_diff3_3(int *exitcode,
                    const char *dir,
                    const char *mine,
Index: subversion/svn/cl.h
===================================================================
--- subversion/svn/cl.h	(revision 1480974)
+++ subversion/svn/cl.h	(working copy)
@@ -184,6 +184,8 @@ typedef struct svn_cl__opt_state_t
     {
   const char *diff_cmd;              /* the external diff command to use
                                         (not converted to UTF-8) */
+  const char *invoke_diff_cmd;       /* the format string to specify args   */
+                                     /* for the external diff cmd           */
   svn_boolean_t internal_diff;       /* override diff_cmd in config file */
   svn_boolean_t no_diff_added;       /* do not show diffs for deleted files */
   svn_boolean_t no_diff_deleted;     /* do not show diffs for deleted files */
Index: subversion/svn/diff-cmd.c
===================================================================
--- subversion/svn/diff-cmd.c	(revision 1480974)
+++ subversion/svn/diff-cmd.c	(working copy)
@@ -404,7 +404,7 @@ svn_cl__diff(apr_getopt_t *os,
                                 ctx, iterpool));
             }
           else
-            SVN_ERR(svn_client_diff6(
+            SVN_ERR(svn_client_diff7(
                      options,
                      target1,
                      &(opt_state->start_revision),
@@ -424,6 +424,7 @@ svn_cl__diff(apr_getopt_t *os,
                      outstream,
                      errstream,
                      opt_state->changelists,
+                     opt_state->diff.invoke_diff_cmd,                        
                      ctx, iterpool));
         }
       else
@@ -455,7 +456,7 @@ svn_cl__diff(apr_getopt_t *os,
                                 ctx, iterpool));
             }
           else
-            SVN_ERR(svn_client_diff_peg6(
+            SVN_ERR(svn_client_diff_peg7(
                      options,
                      truepath,
                      &peg_revision,
@@ -475,6 +476,7 @@ svn_cl__diff(apr_getopt_t *os,
                      outstream,
                      errstream,
                      opt_state->changelists,
+                     opt_state->diff.invoke_diff_cmd,
                      ctx, iterpool));
         }
     }
Index: subversion/svn/svn.c
===================================================================
--- subversion/svn/svn.c	(revision 1480974)
+++ subversion/svn/svn.c	(working copy)
@@ -84,6 +84,7 @@ typedef enum svn_cl__longopt_t {
   opt_ignore_properties,
   opt_properties_only,
   opt_patch_compatible,
+  opt_invoke_diff_cmd,
   /* end of diff options */
   opt_dry_run,
   opt_editor_cmd,
@@ -336,6 +337,25 @@ const apr_getopt_option_t svn_cl__options[] =
   {"diff", opt_diff, 0, N_("produce diff output")}, /* maps to show_diff */
   /* diff options */
   {"diff-cmd",      opt_diff_cmd, 1, N_("use ARG as diff command")},
+  {"invoke-diff-cmd",      opt_invoke_diff_cmd, 1, 
+                   N_("use ARG as format string for external diff command\n"
+                      "                             "
+                      "invocation. \n                                         \n" 
+                      "                             "
+                      "Substitutions: %f1% %f2%  files to compare             \n"
+                      "                             "
+                      "               %l1% %l2%  user defined labels          \n"
+                      "                             "
+                      "Examples: --invoke-diff-cmd=\"diff -y %f1% %f2%        \n"          
+                      "                             "
+                      "   --invoke-diff-cmd=\"kdiff3 -auto -o /home/u/log \\  \n"
+                      "                             "
+                      "     %f1% %f2% --L1 %l1% --L2 \"Custom Label\" \"      \n"
+                      "                             "
+                      "The switch symbol '%' can be escaped in the usual way  \n"          
+                      "                             "
+                      "using the '%' character: %%f1% will be passed as %f1%. \n"          
+     )},
   {"internal-diff", opt_internal_diff, 0,
                        N_("override diff-cmd specified in config file")},
   {"no-diff-added", opt_no_diff_added, 0,
@@ -575,7 +595,8 @@ const svn_opt_subcommand_desc2_t svn_cl__cmd_table
      opt_internal_diff, 'x', opt_no_diff_added, opt_no_diff_deleted,
      opt_ignore_properties, opt_properties_only,
      opt_show_copies_as_adds, opt_notice_ancestry, opt_summarize, opt_changelist,
-     opt_force, opt_xml, opt_use_git_diff_format, opt_patch_compatible} },
+     opt_force, opt_xml, opt_use_git_diff_format, opt_patch_compatible,
+     opt_invoke_diff_cmd} },
   { "export", svn_cl__export, {0}, N_
     ("Create an unversioned copy of a tree.\n"
      "usage: 1. export [-r REV] URL[@PEGREV] [PATH]\n"
@@ -2093,6 +2114,9 @@ sub_main(int argc, const char *argv[], apr_pool_t
       case opt_diff_cmd:
         opt_state.diff.diff_cmd = apr_pstrdup(pool, opt_arg);
         break;
+      case opt_invoke_diff_cmd:
+        opt_state.diff.invoke_diff_cmd = apr_pstrdup(pool, opt_arg);
+        break;
       case opt_merge_cmd:
         opt_state.merge_cmd = apr_pstrdup(pool, opt_arg);
         break;
@@ -2502,6 +2526,14 @@ sub_main(int argc, const char *argv[], apr_pool_t
       return EXIT_ERROR(err);
     }
 
+  if (opt_state.diff.invoke_diff_cmd && opt_state.diff.internal_diff)
+    {
+      err = svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+                             _("--invoke-diff-cmd and --internal-diff "
+                               "are mutually exclusive"));
+      return EXIT_ERROR(err);
+    }
+
   /* Ensure that 'revision_ranges' has at least one item, and make
      'start_revision' and 'end_revision' match that item. */
   if (opt_state.revision_ranges->nelts == 0)
@@ -2714,9 +2746,17 @@ sub_main(int argc, const char *argv[], apr_pool_t
 
   /* XXX: Only diff_cmd for now, overlay rest later and stop passing
      opt_state altogether? */
-  if (opt_state.diff.diff_cmd)
+  if (opt_state.diff.diff_cmd) 
+   {
     svn_config_set(cfg_config, SVN_CONFIG_SECTION_HELPERS,
                    SVN_CONFIG_OPTION_DIFF_CMD, opt_state.diff.diff_cmd);
+   }
+  else 
+    {
+      if (opt_state.diff.invoke_diff_cmd)
+        svn_config_set(cfg_config, SVN_CONFIG_SECTION_HELPERS,
+                       SVN_CONFIG_OPTION_INVOKE_DIFF_CMD, opt_state.diff.invoke_diff_cmd);
+    }
   if (opt_state.merge_cmd)
     svn_config_set(cfg_config, SVN_CONFIG_SECTION_HELPERS,
                    SVN_CONFIG_OPTION_DIFF3_CMD, opt_state.merge_cmd);
Index: subversion/tests/cmdline/diff_tests.py
===================================================================
--- subversion/tests/cmdline/diff_tests.py	(revision 1480974)
+++ subversion/tests/cmdline/diff_tests.py	(working copy)
@@ -3260,7 +3260,30 @@ def diff_external_diffcmd(sbox):
                                      'diff', '--diff-cmd', diff_script_path,
                                      iota_path)
 
+# Check the correct parsing of arguments for an external diff tool
+def diff_invoke_external_diffcmd(sbox):
+  "svn diff --diff-invoke-cmd passes correct args"
 
+  sbox.build(read_only = True)
+  os.chdir(sbox.wc_dir)
+
+  iota_path = 'iota'
+  svntest.main.file_append(iota_path, "new text in iota")
+
+  expected_output = svntest.verify.ExpectedOutput([
+      "Index: iota\n",
+      "===================================================================\n",
+      "Dumping @ARGV...\n",
+      "Arg 0 is >iota	(revision 1)<\n",
+      "Arg 1 is >"+os.path.abspath(svntest.wc.text_base_path("iota")) + "<\n",
+      "Arg 2 is >iota	(working copy)<\n",
+      "Arg 3 is >"+os.path.abspath("iota") + "<\n"])
+
+  svntest.actions.run_and_verify_svn(None, expected_output, [],
+   'diff',
+   '--invoke-diff-cmd=/home/g/trunk_ziff/tools/hook-scripts/argv_dump.pl %l1% %f1% %l2% %f2%',
+   iota_path)
+
 #----------------------------------------------------------------------
 # Diffing an unrelated repository URL against working copy with
 # local modifications (i.e. not committed). This is issue #3295 (diff
@@ -4560,6 +4583,7 @@ test_list = [ None,
               diff_file_depth_empty,
               diff_wrong_extension_type,
               diff_external_diffcmd,
+              diff_invoke_external_diffcmd,
               diff_url_against_local_mods,
               diff_preexisting_rev_against_local_add,
               diff_git_format_wc_wc,
Index: tools/hook-scripts/argv_dump.pl
===================================================================
--- tools/hook-scripts/argv_dump.pl	(revision 0)
+++ tools/hook-scripts/argv_dump.pl	(working copy)
@@ -0,0 +1,33 @@
+#!/usr/bin/perl -w
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+#
+# Use this script to test svn output.  For example:
+#
+# svn diff /
+# '--invoke-diff-cmd=/home/g/trunk_ziff/tools/hook_scripts/argv_dump.pl %f1% %f2%'
+#
+use strict;
+use warnings;
+
+print "Dumping \@ARGV...\n";
+for (my $i = 0; $i < @ARGV; $i++) {
+    print "Arg $i is >$ARGV[$i]<\n";
+}
+exit 0;

Property changes on: tools/hook-scripts/argv_dump.pl
___________________________________________________________________
Added: svn:executable
## -0,0 +1 ##
+*
\ No newline at end of property

Reply via email to