Take two.

Thanks for looking!

Gabriela

[[[
Add new diff option "--invoke-diff-cmd" which allows the user to
define a custom command line or config file entry for an external
diff program.

* subversion/include/svn_client.h
   (svn_client_diff6): Deprecate. Add new Doxygen comment.      
   (svn_client_diff7): Add new Doxygen comment.
   (svn_client_diff7): New function.
   (svn_client_diff_peg_6): Deprecate. Refresh Doxygen comment.
   (svn_client_diff_peg_7): New function.

* subversion/include/svn_config.h
   (SVN_CONFIG_OPTION_INVOKE_DIFF_CMD): New definition.

* subversion/include/svn_io.h
   (svn_io_create_custom_diff_cmd): New function.
   (svn_io_run_external_diff): New function.

* subversion/libsvn_client/deprecated.c
   (svn_client_diff6): Deprecate.
   (svn_client_diff_peg6): Deprecate.

* subversion/libsvn_client/diff.c
   (struct diff_cmd_baton): New variable.

   (diff_content_changed): Call svn_io_run_external_diff if
   --invoke-diff-cmd option was specied, otherwise retain previous
   behaviour.

   (set_up_diff_cmd_and_options): Apply invoke-diff-cmd option
   preferentially.  Old behavior unchanged.
   (svn_client_diff_peg_7): New function.

   (svn_client_diff6): Deprecate.
   (svn_client_diff7): New function.

* subversion/libsvn_subr/config_file.c
   (svn_config_ensure): Add help text.

* subversion/libsvn_subr/io.c
   (svn_io_create_custom_diff_cmd): New function.
   (svn_io_run_external_diff): New function.

* subversion/svn/cl.h
   (struct svn_cl__opt_state_t):  New struct member.
   (struct svn_cl__opt_state_t.diff): New struct member.

* subversion/svn/diff-cmd.c
   (svn_cl__diff): Modify call to deprecated function.

* subversion/svn/svn.c
   (svn_cl__options[]): New variable and help info.
   (svn_cl__cmd_table[]): New variable.
   (sub_main): Add exclusiveness condition.  Expand if condition.
]]]
Index: subversion/include/svn_client.h
===================================================================
--- subversion/include/svn_client.h	(revision 1475745)
+++ subversion/include/svn_client.h	(working copy)
@@ -2986,6 +2986,9 @@ svn_client_blame(const char *path_or_url,
  * The above two options are mutually exclusive. It is an error to set
  * both to TRUE.
  *
+ * If @a invoke_diff_cmd is non-null, invoke external diff command
+ * with the string it contains.
+ *
  * Generated headers are encoded using @a header_encoding.
  *
  * Diff output will not be generated for binary files, unless @a
@@ -3015,9 +3018,39 @@ 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.8.
+ */
+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,
+                 svn_client_ctx_t *ctx,
+                 apr_pool_t *pool,
+                 const char *invoke_diff_cmd);
+
+/** 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 +3208,46 @@ 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.
  */
 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,
+                     svn_client_ctx_t *ctx,
+                     apr_pool_t *pool,
+                     const char *invoke_diff_cmd);
+
+/** 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.8.
+ */
+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 1475745)
+++ subversion/include/svn_config.h	(working copy)
@@ -112,6 +112,7 @@ 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"
+#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 1475745)
+++ subversion/include/svn_io.h	(working copy)
@@ -2273,8 +2273,39 @@ svn_io_file_readline(apr_file_t *file,
                      apr_pool_t *result_pool,
                      apr_pool_t *scratch_pool);
 
+/*  Parse a user defined command to contain dynamically 
+ *  created labels and filenames.
+ *  
+ ** @since New in 1.8. (?)
+ */
 /** @} */
+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.8.
+ */
+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 1475745)
+++ subversion/libsvn_client/deprecated.c	(working copy)
@@ -914,6 +914,54 @@ 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,
+                          ctx, 
+                          pool,
+                          NULL);
+}
+
+svn_error_t *
 svn_client_diff5(const apr_array_header_t *diff_options,
                  const char *path1,
                  const svn_opt_revision_t *revision1,
@@ -1036,6 +1084,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,
+                              ctx,
+                              pool,
+                              NULL);
+}
+
+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 1475745)
+++ subversion/libsvn_client/diff.c	(working copy)
@@ -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));
 
@@ -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,
@@ -2557,7 +2584,8 @@ svn_error_t *
                  svn_stream_t *errstream,
                  const apr_array_header_t *changelists,
                  svn_client_ctx_t *ctx,
-                 apr_pool_t *pool)
+                 apr_pool_t *pool,
+                 const char *invoke_diff_cmd)
 {
   struct diff_cmd_baton diff_cmd_baton = { 0 };
   svn_opt_revision_t peg_revision;
@@ -2573,9 +2601,10 @@ 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));
+                                        ctx->config, pool));
   diff_cmd_baton.pool = pool;
   diff_cmd_baton.outstream = outstream;
   diff_cmd_baton.errstream = errstream;
@@ -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,
@@ -2624,7 +2653,8 @@ svn_error_t *
                      svn_stream_t *errstream,
                      const apr_array_header_t *changelists,
                      svn_client_ctx_t *ctx,
-                     apr_pool_t *pool)
+                     apr_pool_t *pool,
+                     const char *invoke_diff_cmd)
 {
   struct diff_cmd_baton diff_cmd_baton = { 0 };
 
@@ -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));
@@ -2721,3 +2752,4 @@ svn_client_diff_summarize_dup(const svn_client_dif
 
   return dup_diff;
 }
+
Index: subversion/libsvn_subr/config_file.c
===================================================================
--- subversion/libsvn_subr/config_file.c	(revision 1475745)
+++ subversion/libsvn_subr/config_file.c	(working copy)
@@ -1084,6 +1084,12 @@ 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 = "                                             NL
+        "###     diff_program (diff, gdiff, etc.) <options> <substitutions>" 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 1475745)
+++ subversion/libsvn_subr/io.c	(working copy)
@@ -2921,8 +2921,164 @@ 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_array_header_t *tmp;
+  svn_stringbuf_t *com;
+  const char ** ret;
+  char * delimiter_prefix;
+  char * default_delimiter_prefix;
+  int  has_custom_delimiter, i;
+  apr_pool_t *subpool;
 
+  subpool = svn_pool_create(pool);
+
+#ifdef WIN32
+  default_delimiter_prefix = apr_pstrdup(subpool, "---");
+#else
+  default_delimiter_prefix = apr_pstrdup(subpool, "%");  
+#endif
+
+  if (cmd[0] == '#' || cmd[0] == '$' || cmd[0] == '-') 
+   {  /* user requests custom delimter prefix */
+     tmp = svn_cstring_split(cmd, " ", FALSE, subpool);
+     delimiter_prefix =  APR_ARRAY_IDX(tmp,0, char *);
+     has_custom_delimiter = 1;
+   }
+  else 
+   {
+     delimiter_prefix = apr_pstrdup(subpool, default_delimiter_prefix);
+     has_custom_delimiter = 0;
+   }
+
+  tmp = svn_cstring_split("f1 f2 f3 l1 l2 l3", " ", TRUE, subpool);
+
+  { /* add the selected prefix to the delimiters in tmp */
+    char *s;
+    s = apr_pcalloc(subpool, (1+
+                              strlen(delimiter_prefix)+
+                              tmp->nelts)*sizeof(char *));
+    
+    for (i = 0; i < tmp->nelts; i++)
+     {
+      strcat(s, delimiter_prefix);
+      strcat(s, APR_ARRAY_IDX(tmp, i, char *));
+      strcat(s," ");
+     
+     }
+    tmp = svn_cstring_split(s, " ", FALSE, subpool);
+  }
+
+  com = svn_stringbuf_create_empty(pool);
+  svn_stringbuf_appendcstr(com, cmd);
+
+  if (has_custom_delimiter) 
+    svn_stringbuf_remove(com, 0,strlen(delimiter_prefix));
+
+  for (i = 0; i < tmp->nelts; i++) 
+    {
+      svn_stringbuf_t *token;
+      char * found;
+ 
+      token = svn_stringbuf_create_empty(subpool);
+      svn_stringbuf_appendcstr(token, APR_ARRAY_IDX(tmp,i,char *));
+
+      while ( (found = strstr(com->data, token->data)) ) 
+        {
+          if (i == 0)
+            svn_stringbuf_replace(com, com->len - strlen(found), token->len, 
+                                  tmpfile1, strlen(tmpfile1));
+          else if (i == 1)
+            svn_stringbuf_replace(com, com->len - strlen(found), token->len, 
+                                  tmpfile2, strlen(tmpfile2));
+          else if (i == 2)
+            svn_stringbuf_replace(com, com->len - strlen(found), token->len, 
+                                  base, strlen(base));
+          else if (i == 3)
+            svn_stringbuf_replace(com, com->len - strlen(found), token->len, 
+                                  label1, strlen(label1));
+          else if (i == 4)
+            svn_stringbuf_replace(com, com->len - strlen(found), token->len, 
+                                  label2, strlen(label2));
+          else if (i == 5)
+            svn_stringbuf_replace(com, com->len - strlen(found), token->len, 
+                                  label3, strlen(label3));
+        }
+    }
+ 
+  tmp = svn_cstring_split(com->data," ",FALSE, 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;
+  apr_pool_t *subpool = svn_pool_create(pool);
+
+  cmd = svn_io_create_custom_diff_cmd(label1, label2, NULL, tmpfile1, tmpfile2,
+                                      NULL, external_diff_cmd, subpool);
+  if (pexitcode == NULL)
+    pexitcode = &exitcode;
+
+  SVN_ERR(svn_io_run_cmd(dir, cmd[0], cmd, pexitcode, NULL, TRUE,
+                         NULL, outfile, errfile, subpool));
+
+  if (*pexitcode != 0 && *pexitcode != 1)
+   {
+       int zip, size;
+       char * failed_command;
+
+       size = 0;
+
+       for (zip = 0; cmd[zip]; zip++) size += strlen(cmd[zip]) + 1;
+       failed_command = apr_pcalloc(subpool, size * sizeof(char *));
+       for (zip = 0; cmd[zip]; zip++) 
+        {
+         strcat(failed_command, cmd[zip]);
+         strcat(failed_command, " ");
+        }
+       return svn_error_createf(SVN_ERR_EXTERNAL_PROGRAM, NULL,
+                                _("'%s' was expanded to %s and returned %d"),
+                                external_diff_cmd,
+                                svn_dirent_local_style(failed_command, subpool),
+                                *pexitcode);
+   }
+  svn_pool_destroy(subpool);
+
+  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 1475745)
+++ subversion/svn/cl.h	(working copy)
@@ -181,6 +181,8 @@ typedef struct svn_cl__opt_state_t
   struct
     {
   const char *diff_cmd;              /* the external diff command to use */
+  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 */
@@ -213,6 +215,7 @@ typedef struct svn_cl__opt_state_t
   const char *changelist;        /* operate on this changelist
                                     THIS IS TEMPORARY (LAST OF CHANGELISTS) */
   svn_boolean_t keep_changelists;/* don't remove changelists after commit */
+  const char *invoke_diff_cmd;   /* external customizable diff command      */
   svn_boolean_t keep_local;      /* delete path only from repository */
   svn_boolean_t all_revprops;    /* retrieve all revprops */
   svn_boolean_t no_revprops;     /* retrieve no revprops */
Index: subversion/svn/diff-cmd.c
===================================================================
--- subversion/svn/diff-cmd.c	(revision 1475745)
+++ subversion/svn/diff-cmd.c	(working copy)
@@ -387,7 +387,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),
@@ -407,7 +407,9 @@ svn_cl__diff(apr_getopt_t *os,
                      outstream,
                      errstream,
                      opt_state->changelists,
-                     ctx, iterpool));
+                     ctx, 
+                     iterpool,
+                     opt_state->diff.invoke_diff_cmd));
         }
       else
         {
@@ -438,7 +440,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,
@@ -458,7 +460,9 @@ svn_cl__diff(apr_getopt_t *os,
                      outstream,
                      errstream,
                      opt_state->changelists,
-                     ctx, iterpool));
+                     ctx, 
+                     iterpool,
+                     opt_state->diff.invoke_diff_cmd));
         }
     }
 
Index: subversion/svn/svn.c
===================================================================
--- subversion/svn/svn.c	(revision 1475745)
+++ 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,55 @@ 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")},
+#ifdef WIN32
+  {"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 modified by defining an   \n"          
+                      "                             "
+                      "alternative symbol string, starting with '#', '$' or '-'\n"          
+                      "                             "
+                      "Example:                                                \n"          
+                      "                             "
+                      "  --invoke-diff-cmd=\"%% diff -y %%f1 %%f2\"            \n"          
+     )},
+#else
+  {"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 modified by defining an   \n"          
+                      "                             "
+                      "alternative symbol string, starting with '#','$' or '-'\n"          
+                      "                             "
+                      "Example:                                               \n"          
+                      "                             "
+                      "  --invoke-diff-cmd=\"--- diff -y ---f1 ---f2\"        \n"          
+     )},
+#endif /* WIN32 */
+
   {"internal-diff", opt_internal_diff, 0,
                        N_("override diff-cmd specified in config file")},
   {"no-diff-added", opt_no_diff_added, 0,
@@ -575,7 +625,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"
@@ -2092,6 +2143,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;
@@ -2497,6 +2551,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)
@@ -2709,9 +2771,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);

Reply via email to