[PATCH] issue #2069 - "svn status" in xml mode - v5

classic Classic list List threaded Threaded
1 message Options
Reply | Threaded
Open this post in threaded view
|

[PATCH] issue #2069 - "svn status" in xml mode - v5

Alexander Thomas
Version 5 of the patch for svn status --xml is attached.

[[[
Patch to fix issue 2069 - "svn status" in xml mode

* subversion/clients/cmdline/cl.h
  (svn_cl__print_status_xml): Added new function prototype.

* subversion/clients/cmdline/status.c
  (generate_status_desc): New function.
  (svn_cl__print_status_xml): New function to handle xml output.

* subversion/clients/cmdline/main.c
  (svn_cl__cmd_table): Added --incremental and --xml options to
  subcommand array.

* subversion/clients/cmdline/status-cmd.c
  (struct status_baton): Added new boolean field.
  (print_start_target_xml): New function.
  (print_header_xml): New function.
  (print_footer_xml): New function.
  (print_finish_target_xml): New function.
  (print_status): Edited to check --xml option.

* subversion/clients/cmdline/dtd/status.dtd
  New file.

* subversion/tests/clients/cmdline/stat_tests.py
  (status_in_xml): New test.
  (test_list): Added status_in_xml.

* tools/client-side/bash_completion
  (_svn):  Add "--incremental" and "--xml" options to "status"
  for auto-completion.
]]]

-Alexander Thomas (AT)

Patch to fix issue 2069 - "svn status" in xml mode

* subversion/clients/cmdline/cl.h
  (svn_cl__print_status_xml): Added new function prototype.

* subversion/clients/cmdline/status.c
  (generate_status_desc): New function.
  (svn_cl__print_status_xml): New function to handle xml output.

* subversion/clients/cmdline/main.c
  (svn_cl__cmd_table): Added --incremental and --xml options to
  subcommand array.

* subversion/clients/cmdline/status-cmd.c
  (struct status_baton): Added new boolean field.
  (print_start_target_xml): New function.
  (print_header_xml): New function.
  (print_footer_xml): New function.
  (print_finish_target_xml): New function.
  (print_status): Edited to check --xml option.

* subversion/clients/cmdline/dtd/status.dtd
  New file.

* subversion/tests/clients/cmdline/stat_tests.py
  (status_in_xml):´┐ŻNew test.
  (test_list): Added status_in_xml.

* tools/client-side/bash_completion
  (_svn):  Add "--incremental" and "--xml" options to "status"
  for auto-completion.

Index: subversion/clients/cmdline/cl.h
===================================================================
--- subversion/clients/cmdline/cl.h (revision 14774)
+++ subversion/clients/cmdline/cl.h (working copy)
@@ -254,6 +254,18 @@
                                    svn_boolean_t repos_locks,
                                    apr_pool_t *pool);
 
+
+/* Print STATUS for PATH in XML to stdout. Unlike svn_cl__print_status,
+   prints details only in verbose mode. Checks status against the
+   latest revision in the repository if the '-u' cmdline option is
+   specified. Like svn_cl__print_status, lists missing repository locks
+   as broken WC locks. */
+svn_error_t *
+svn_cl__print_status_xml (const char *path,
+                          svn_wc_status2_t *status,
+                          apr_pool_t *pool);
+
+
 /* Print a hash that maps property names (char *) to property values
    (svn_string_t *).  The names are assumed to be in UTF-8 format;
    the values are either in UTF-8 (the special Subversion props) or
Index: subversion/clients/cmdline/status.c
===================================================================
--- subversion/clients/cmdline/status.c (revision 14774)
+++ subversion/clients/cmdline/status.c (working copy)
@@ -24,6 +24,8 @@
 #include "svn_cmdline.h"
 #include "svn_wc.h"
 #include "svn_path.h"
+#include "svn_xml.h"
+#include "svn_time.h"
 #include "cl.h"
 
 
@@ -51,6 +53,32 @@
     }
 }
 
+
+/* Return the detailed string representation of STATUS */
+static const char*
+generate_status_desc (enum svn_wc_status_kind status)
+{
+  switch (status)
+    {
+    case svn_wc_status_none:        return "none";
+    case svn_wc_status_normal:      return "normal";
+    case svn_wc_status_added:       return "added";
+    case svn_wc_status_missing:     return "missing";
+    case svn_wc_status_incomplete:  return "incomplete";
+    case svn_wc_status_deleted:     return "deleted";
+    case svn_wc_status_replaced:    return "replaced";
+    case svn_wc_status_modified:    return "modified";
+    case svn_wc_status_merged:      return "merged";
+    case svn_wc_status_conflicted:  return "conflicted";
+    case svn_wc_status_obstructed:  return "obstructed";
+    case svn_wc_status_ignored:     return "ignored";
+    case svn_wc_status_external:    return "external";
+    case svn_wc_status_unversioned: return "unversioned";
+    default:                        abort();
+    }
+}
+
+
 /* Print STATUS and PATH in a format determined by DETAILED and
    SHOW_LAST_COMMITTED. */
 static svn_error_t *
@@ -166,6 +194,142 @@
   return SVN_NO_ERROR;
 }
 
+
+/* Print STATUS of PATH in XML format */
+svn_error_t *
+svn_cl__print_status_xml (const char *path,
+                          svn_wc_status2_t *status,
+                          apr_pool_t *pool)
+{
+  svn_stringbuf_t *sb = svn_stringbuf_create ("", pool);
+
+  if (status->text_status == svn_wc_status_none
+      && status->repos_text_status == svn_wc_status_none)
+    return SVN_NO_ERROR;
+
+  /* "<entry>" */
+  svn_xml_make_open_tag (&sb, pool, svn_xml_normal, "entry",
+                         "path", svn_path_local_style (path, pool), NULL);
+
+  /* "<wc-status ... >" */
+  apr_hash_t *att_hash = svn_xml_make_att_hash (NULL, pool);
+  apr_hash_set (att_hash, "item", APR_HASH_KEY_STRING,
+                generate_status_desc (status->text_status));
+  apr_hash_set (att_hash, "props", APR_HASH_KEY_STRING,
+                generate_status_desc (status->prop_status));
+  if (status->locked)
+    apr_hash_set (att_hash, "wc-locked", APR_HASH_KEY_STRING, "true");
+  if (status->copied)
+    apr_hash_set (att_hash, "copied", APR_HASH_KEY_STRING, "true");
+  if (status->switched)
+    apr_hash_set (att_hash, "switched", APR_HASH_KEY_STRING, "true");
+  svn_xml_make_open_tag_hash (&sb, pool, svn_xml_normal, "wc-status",
+                              att_hash);
+
+  if (status->entry && status->entry->lock_token)
+    {
+      svn_xml_make_open_tag (&sb, pool, svn_xml_normal, "lock", NULL);
+
+      svn_xml_make_open_tag (&sb, pool, svn_xml_protect_pcdata,
+                             "token", NULL);
+      svn_xml_escape_cdata_cstring (&sb, status->entry->lock_token, pool);
+      svn_xml_make_close_tag (&sb, pool, "token");
+
+      /* If lock_owner is NULL, assume WC is corrupt. */
+      if (status->entry->lock_owner)
+        {
+          svn_xml_make_open_tag (&sb, pool, svn_xml_protect_pcdata,
+                                 "owner", NULL);
+          svn_xml_escape_cdata_cstring (&sb, status->entry->lock_owner,
+                                        pool);
+          svn_xml_make_close_tag (&sb, pool, "owner");
+        }
+      else
+        return svn_error_create (SVN_ERR_WC_CORRUPT, 0, NULL);
+
+      if (status->entry->lock_comment)
+        {
+          svn_xml_make_open_tag (&sb, pool, svn_xml_normal, "comment", NULL);
+          svn_xml_escape_cdata_cstring (&sb, status->entry->lock_comment,
+                                        pool);
+          svn_xml_make_close_tag (&sb, pool, "comment");
+        }
+
+      svn_xml_make_open_tag (&sb, pool, svn_xml_protect_pcdata, "created",
+                             NULL);
+      svn_xml_escape_cdata_cstring (&sb,
+                                    svn_time_to_cstring
+                                     (status->entry->lock_creation_date, pool),
+                                    pool);
+      svn_xml_make_close_tag (&sb, pool, "created");
+
+      svn_xml_make_close_tag (&sb, pool, "lock");
+    }
+
+  /* "</wc-status>" */
+  svn_xml_make_close_tag (&sb, pool, "wc-status");
+
+  if (status->repos_lock)
+    {
+      /* "<repos-status>" */
+      svn_xml_make_open_tag (&sb, pool, svn_xml_normal, "repos-status",
+                             "item",
+                             generate_status_desc (status->repos_text_status),
+                             "props",
+                             generate_status_desc (status->repos_prop_status),
+                             NULL);
+
+      /* "<lock>" */
+      svn_xml_make_open_tag (&sb, pool, svn_xml_normal, "lock", NULL);
+
+      svn_xml_make_open_tag (&sb, pool, svn_xml_protect_pcdata, "token", NULL);
+      svn_xml_escape_cdata_cstring (&sb, status->repos_lock->token, pool);
+      svn_xml_make_close_tag (&sb, pool, "token");
+
+      svn_xml_make_open_tag (&sb, pool, svn_xml_protect_pcdata, "owner", NULL);
+      svn_xml_escape_cdata_cstring (&sb, status->repos_lock->owner, pool);
+      svn_xml_make_close_tag (&sb, pool, "owner");
+
+      if (status->repos_lock->comment)
+        {
+          /* "<comment>xx</comment>" */
+          svn_xml_make_open_tag (&sb, pool, svn_xml_normal, "comment", NULL);
+          svn_xml_escape_cdata_cstring (&sb, status->repos_lock->comment, pool);
+          svn_xml_make_close_tag (&sb, pool, "comment");
+        }
+
+     /* "<created>xx</created>" */
+      svn_xml_make_open_tag (&sb, pool, svn_xml_protect_pcdata,
+                             "created", NULL);
+      svn_xml_escape_cdata_cstring (&sb, svn_time_to_cstring
+                                    (status->repos_lock->creation_date,
+                                     pool),
+                                    pool);
+      svn_xml_make_close_tag (&sb, pool, "created");
+
+      if (status->repos_lock->expiration_date != 0)
+        {
+          svn_xml_make_open_tag (&sb, pool, svn_xml_protect_pcdata,
+                                 "expires", NULL);
+          svn_xml_escape_cdata_cstring (&sb, svn_time_to_cstring
+                                        (status->repos_lock->expiration_date,
+                                         pool),
+                                        pool);
+          svn_xml_make_close_tag (&sb, pool, "expires");
+        }
+
+      /* "</lock>" */
+      svn_xml_make_close_tag (&sb, pool, "lock");
+
+      /* "</repos-status>"*/
+      svn_xml_make_close_tag (&sb, pool, "repos-status");
+    }
+
+  /* "</entry>" */
+  svn_xml_make_close_tag (&sb, pool, "entry");
+  return svn_cl__error_checked_fputs (sb->data, stdout);
+}
+
 /* Called by status-cmd.c */
 svn_error_t *
 svn_cl__print_status (const char *path,
Index: subversion/clients/cmdline/main.c
===================================================================
--- subversion/clients/cmdline/main.c (revision 14774)
+++ subversion/clients/cmdline/main.c (working copy)
@@ -682,8 +682,9 @@
      "    A  +         965       687 joe          wc/qax.c\n"
      "                 965       687 joe          wc/zig.c\n"
      "    Status against revision:   981\n"),
-    { 'u', 'v', 'N', 'q', svn_cl__no_ignore_opt, SVN_CL__AUTH_OPTIONS,
-      svn_cl__config_dir_opt, svn_cl__ignore_externals_opt} },
+    { 'u', 'v', 'N', 'q', svn_cl__no_ignore_opt, svn_cl__incremental_opt,
+      svn_cl__xml_opt, SVN_CL__AUTH_OPTIONS, svn_cl__config_dir_opt,
+      svn_cl__ignore_externals_opt} },
   
   { "switch", svn_cl__switch, {"sw"},
     N_("Update the working copy to a different URL.\n"
Index: subversion/clients/cmdline/status-cmd.c
===================================================================
--- subversion/clients/cmdline/status-cmd.c (revision 14774)
+++ subversion/clients/cmdline/status-cmd.c (working copy)
@@ -26,8 +26,13 @@
 #include "svn_client.h"
 #include "svn_error.h"
 #include "svn_pools.h"
+#include "svn_xml.h"
+#include "svn_path.h"
 #include "cl.h"
 
+#include "svn_private_config.h"
+
+
 
 /*** Code. ***/
 
@@ -43,9 +48,73 @@
 
   svn_boolean_t had_print_error;  /* To avoid printing lots of errors if we get
                                      errors while printing to stdout */
+  svn_boolean_t xml_mode;
 };
 
 
+/* Prints XML target */
+static svn_error_t *
+print_start_target_xml (apr_pool_t *pool, const char *target)
+{
+  svn_stringbuf_t *sb = svn_stringbuf_create ("", pool);
+
+  /* "<target ...>" */
+  svn_xml_make_open_tag (&sb, pool, svn_xml_normal, "target",
+                         "path", target, NULL);
+
+  return svn_cl__error_checked_fputs (sb->data, stdout);
+}
+
+
+/* Prints XML header */
+static svn_error_t *
+print_header_xml (apr_pool_t *pool)
+{
+  svn_stringbuf_t *sb = svn_stringbuf_create ("", pool);
+
+  svn_xml_make_header (&sb, pool);
+  svn_xml_make_open_tag (&sb, pool, svn_xml_normal, "status", NULL);
+
+  return svn_cl__error_checked_fputs (sb->data, stdout);
+}
+
+
+/* Prints XML footer */
+static svn_error_t *
+print_footer_xml (apr_pool_t *pool)
+{
+  svn_stringbuf_t *sb = svn_stringbuf_create ("", pool);
+
+  svn_xml_make_close_tag (&sb, pool, "status");
+
+  return svn_cl__error_checked_fputs (sb->data, stdout);
+}
+
+/* Prints </target> and the revision in repository against which
+ * status was checked */
+static svn_error_t *
+print_finish_target_xml (apr_pool_t *pool,
+                         svn_revnum_t repos_rev,
+                         svn_boolean_t update)
+{
+  svn_stringbuf_t *sb = svn_stringbuf_create ("", pool);
+
+  if (SVN_IS_VALID_REVNUM (repos_rev) && update)
+    {
+      const char *repos_rev_str;
+      repos_rev_str = apr_psprintf (pool, "%" APR_OFF_T_FMT, repos_rev);
+      /* "<against ... />" */
+      svn_xml_make_open_tag (&sb, pool, svn_xml_self_closing, "against",
+                             "revision", repos_rev_str, NULL);
+    }
+
+  /* "</target>" */
+  svn_xml_make_close_tag (&sb, pool, "target");
+
+  return svn_cl__error_checked_fputs (sb->data, stdout);
+}
+
+
 /* A status callback function for printing STATUS for PATH. */
 static void
 print_status (void *baton,
@@ -54,12 +123,16 @@
 {
   struct status_baton *sb = baton;
   svn_error_t *err;
-  
-  err = svn_cl__print_status (path, status, sb->detailed,
-                              sb->show_last_committed,
-                              sb->skip_unrecognized, sb->repos_locks,
-                              sb->pool);
 
+  if (sb->xml_mode)
+    err = svn_cl__print_status_xml (path, status, sb->pool);
+  else
+    err = svn_cl__print_status (path, status, sb->detailed,
+                                sb->show_last_committed,
+                                sb->skip_unrecognized,
+                                sb->repos_locks,
+                                sb->pool);
+
   if (err)
     {
       /* Print if it is the first error. */
@@ -86,6 +159,8 @@
   int i;
   svn_opt_revision_t rev;
   struct status_baton sb;
+  svn_revnum_t repos_rev;
+  svn_error_t *err;
 
   SVN_ERR (svn_opt_args_to_target_array2 (&targets, os,
                                           opt_state->targets, pool));
@@ -93,9 +168,10 @@
   /* We want our -u statuses to be against HEAD. */
   rev.kind = svn_opt_revision_head;
 
-  /* The notification callback. */
-  svn_cl__get_notifier (&ctx->notify_func2, &ctx->notify_baton2, FALSE, FALSE,
-                        FALSE, pool);
+  /* The notification callback, set the notifier to NULL in XML mode */
+  if (! opt_state->xml)
+    svn_cl__get_notifier (&ctx->notify_func2, &ctx->notify_baton2, FALSE,
+                          FALSE, FALSE, pool);
 
   /* Add "." if user passed 0 arguments */
   svn_opt_push_implicit_dot_target(targets, pool);
@@ -104,6 +180,23 @@
 
   sb.had_print_error = FALSE;
 
+  if (opt_state->xml)
+    {
+      /* If output is not incremental, output the XML header and wrap
+         everything in a top-level element. This makes the output in
+         its entirety a well-formed XML document. */
+      if (! opt_state->incremental)
+        SVN_ERR (print_header_xml (pool));
+
+    }
+  else
+    {
+      if (opt_state->incremental)
+        return svn_error_create (SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+                                 _("'incremental' option only valid in XML "
+                                   "mode"));
+    }
+
   for (i = 0; i < targets->nelts; i++)
     {
       const char *target = ((const char **) (targets->elts))[i];
@@ -115,21 +208,34 @@
       /* Retrieve a hash of status structures with the information
          requested by the user. */
 
-      sb.detailed = (opt_state->verbose || opt_state->update);
-      sb.show_last_committed = opt_state->verbose;
+      sb.detailed = (opt_state->verbose || opt_state->update
+                     || opt_state->xml);
+      sb.show_last_committed = (opt_state->verbose || opt_state->xml);
       sb.skip_unrecognized = opt_state->quiet;
       sb.repos_locks = opt_state->update;
+      sb.xml_mode = opt_state->xml;
       sb.pool = subpool;
-      SVN_ERR (svn_client_status2 (NULL, target, &rev, print_status, &sb,
-                                   opt_state->nonrecursive ? FALSE : TRUE,
-                                   opt_state->verbose,
-                                   opt_state->update,
-                                   opt_state->no_ignore,
-                                   opt_state->ignore_externals,
-                                   ctx, subpool));
+
+      if (opt_state->xml)
+        print_start_target_xml (pool, svn_path_local_style (target, pool));
+
+      err = svn_client_status2 (&repos_rev, target, &rev, print_status, &sb,
+                                opt_state->nonrecursive ? FALSE : TRUE,
+                                (opt_state->verbose || opt_state->xml),
+                                opt_state->update,
+                                opt_state->no_ignore,
+                                opt_state->ignore_externals,
+                                ctx, subpool);
+      if (opt_state->xml)
+        err = print_finish_target_xml (pool, repos_rev, opt_state->update);
+
+      if (err)
+        break;
     }
 
   svn_pool_destroy (subpool);
-  
-  return SVN_NO_ERROR;
+  if (opt_state->xml && (! opt_state->incremental))
+    err = print_footer_xml (pool);
+
+  return err;
 }
Index: subversion/tests/clients/cmdline/stat_tests.py
===================================================================
--- subversion/tests/clients/cmdline/stat_tests.py (revision 14774)
+++ subversion/tests/clients/cmdline/stat_tests.py (working copy)
@@ -817,6 +817,41 @@
   svntest.actions.run_and_verify_status(wc_dir, expected_status)
 
 
+def status_in_xml(sbox):
+  "status output in XML format"
+
+  sbox.build()
+  wc_dir = sbox.wc_dir
+
+  file_name = "iota"
+  file_path = os.path.join (wc_dir, file_name)
+  svntest.main.file_append(file_path, "This line added in iota to test svn st --xml\n")
+
+  template = ["<?xml version=\"1.0\" encoding=\"utf-8\"?>\n",
+              "<status>\n",
+              "<target\n",
+              "   path=\"%s\">\n" % (file_path),
+              "<entry\n",
+              "   path=\"%s\">\n" % (file_path),
+              "<wc-status\n",
+              "   props=\"none\"\n",
+              "   item=\"modified\">\n",
+              "</wc-status>\n",
+              "</entry>\n",
+              "<against\n",
+              "   revision=\"1\"/>\n",
+              "</target>\n",
+              "</status>\n",
+             ]
+
+  output, error = svntest.actions.run_and_verify_svn (None, None, [],
+                                                      'status', file_path, '--xml', '-u')
+
+  for i in range(0, len(output)):
+    if output[i] != template[i]:
+      print "ERROR: expected:", template[i], "actual:", output[i]
+      raise svntest.Failure
+
 #----------------------------------------------------------------------  
 
 
@@ -842,6 +877,7 @@
               status_on_unversioned_dotdot,
               status_on_partially_nonrecursive_wc,
               missing_dir_in_anchor,
+              status_in_xml,
              ]
 
 if __name__ == '__main__':
Index: tools/client-side/bash_completion
===================================================================
--- tools/client-side/bash_completion (revision 14774)
+++ tools/client-side/bash_completion (working copy)
@@ -148,7 +148,7 @@
  ;;
  status|stat|st)
  cmdOpts="-u --show-updates -v --verbose $nOpts $qOpts $pOpts \
-         --no-ignore --ignore-externals"
+         --no-ignore --ignore-externals --incremental --xml"
  ;;
  switch|sw)
  cmdOpts="--relocate $rOpts $nOpts $qOpts $pOpts --diff3-cmd"


---------------------------------------------------------------------
To unsubscribe, e-mail: [hidden email]
For additional commands, e-mail: [hidden email]