Index: subversion/clients/cmdline/unlock-cmd.c
===================================================================
--- subversion/clients/cmdline/unlock-cmd.c	(revision 0)
+++ subversion/clients/cmdline/unlock-cmd.c	(revision 0)
@@ -0,0 +1,207 @@
+/*
+ * unlock-cmd.c -- Unlock files
+ *
+ * ====================================================================
+ * Copyright (c) 2000-2004 CollabNet.  All rights reserved.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution.  The terms
+ * are also available at http://subversion.tigris.org/license-1.html.
+ * If newer versions of this license are posted there, you may use a
+ * newer version instead, at your option.
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals.  For exact contribution history, see the revision
+ * history and logs, available at http://subversion.tigris.org/.
+ * ====================================================================
+ */
+
+/* ==================================================================== */
+
+
+
+/*** Includes. ***/
+
+#include "svn_private_config.h"
+#include "svn_cmdline.h"
+#include "svn_wc.h"
+#include "svn_pools.h"
+#include "svn_client.h"
+#include "svn_string.h"
+#include "svn_path.h"
+#include "svn_delta.h"
+#include "svn_error.h"
+#include "svn_utf.h"
+#include "svn_subst.h"
+#include "svn_private_config.h"
+#include "svn_auth.h"
+#include "svn_ra.h"
+#include "svn_base64.h"
+#include "cl.h"
+
+#include "svn_private_config.h"
+
+
+/*** Code. ***/
+
+/* This implements the `svn_opt_subcommand_t' interface. */
+svn_error_t *
+svn_cl__unlock (apr_getopt_t *os,
+                  void *baton,
+                  apr_pool_t *pool)
+{
+  svn_cl__opt_state_t *opt_state = ((svn_cl__cmd_baton_t *) 
baton)->opt_state;
+  svn_client_ctx_t *ctx = ((svn_cl__cmd_baton_t *) baton)->ctx;
+  apr_pool_t *subpool = svn_pool_create (pool);
+  const char *pname, *pname_utf8, *URL;
+  apr_array_header_t *args, *targets, *condensed_targets;
+  int i, j;
+
+  /* RA stuff */
+  void *ra_baton, *session, *report_baton;
+  svn_ra_plugin_t *ra_lib;
+  const char* reposPath;
+  svn_revnum_t rev;
+  svn_string_t *cur_lock_owner;
+
+
+  /* Suck up all the remaining arguments into a targets array */
+  SVN_ERR (svn_opt_args_to_target_array (&targets, os,
+                                         opt_state->targets,
+                                         &(opt_state->start_revision),
+                                         &(opt_state->end_revision),
+                                         FALSE, pool));
+
+  /* Add "." if user passed 0 arguments. */
+  svn_opt_push_implicit_dot_target (targets, pool);
+
+  /* Remove redundancies from the target list while preserving order. */
+  SVN_ERR (svn_path_remove_redundancies (&condensed_targets,
+                                         targets,
+                                         pool));
+
+  /** init RA layer lib */
+  SVN_ERR (svn_ra_init_ra_libs (&ra_baton, pool));
+
+  for (i = 0; i < condensed_targets->nelts; i++)
+    {
+      const char *target = ((const char **) (targets->elts))[i];
+      svn_wc_adm_access_t *adm_access;
+      const svn_wc_entry_t *entry;
+
+      svn_pool_clear (subpool);
+      SVN_ERR(svn_cl__check_cancel (ctx->cancel_baton));
+
+      /* Make sure we're attempting to unlock a versioned resource */
+      SVN_ERR (svn_wc_adm_probe_open2 (&adm_access, NULL, target, FALSE,
+                                        0, subpool));
+
+      SVN_ERR (svn_wc_entry (&entry, target, adm_access, FALSE, subpool));
+      if (! entry)
+        {
+          SVN_ERR (svn_cmdline_printf
+                    (subpool, _("Could not unlock %s: Not a versioned 
resource\n"),
+                    svn_path_local_style (target, pool)));
+          continue;
+        }
+      else if (entry->kind == svn_node_dir)
+        {
+          SVN_ERR(svn_cmdline_printf( subpool,
+                                      _("Could not unlock '%s': Directories 
can not be locked\n"),
+                                      target));
+          continue;
+        }
+
+
+      /* Convert URL */
+      SVN_ERR (svn_client_url_from_path (&URL, target, subpool));
+      if (URL == NULL)
+        {
+            SVN_ERR(svn_cmdline_printf(subpool, _("Unable to unlock 
'%s'\n"), target));
+            continue;
+        }
+
+      /* Get the RA library that handles this URL schema. */
+      SVN_ERR (svn_ra_get_ra_library (&ra_lib, ra_baton, URL, subpool));
+
+      /* Open a repository session to the URL. */
+      svn_error_t * ra_err = (svn_error_t * )(svn_client__open_ra_session 
(&session, ra_lib, URL, NULL,
+                                            NULL, NULL, TRUE, TRUE,
+                                            ctx, subpool));
+
+      /* Get root part of URL, e.g. http://myserver/svn/myrepos */
+      ra_lib->get_repos_root(session, &reposPath, subpool);
+
+      /* Get path relative to repos-root,
+        i.e. from 'http://myserver/svn/repos/my/path' to 'my/path' */
+      svn_string_t *repos_rel_path = svn_string_create((const 
char*)&URL[strlen(reposPath)+1], subpool);
+
+      /* Base64 encode and get rid of =+\n to make the propname "human 
readable" */
+      svn_stringbuf_t *encoded_path = svn_stringbuf_create_from_string(
+                                                          
svn_base64_encode_string(repos_rel_path, subpool), subpool);
+      for (j=0; j < encoded_path->len; j++)
+        {
+          encoded_path->data[j] = ( encoded_path->data[j] == '\n' ||
+                                    encoded_path->data[j] == '='  ||
+                                    encoded_path->data[j] == '+')
+                                    ? '-' : encoded_path->data[j];
+        }
+
+      /* Create property name in the form Rlock-BASE64(/path/in/repos)
+         The R prefix means that the server-side hook removes the revprop,
+         and hence removes the revprop and hence removes the lock.
+
+         The prefix trick is due to a bug in pre-1.1 that prevents 
pre-revprop-change
+         scripts of getting hold of the propvalue. Once the fix for that
+         is released, a nicer solution might be to pass the force
+         flag as the propvalue. */
+      svn_stringbuf_t *lock_token = svn_stringbuf_create("R", subpool);
+      svn_stringbuf_appendcstr(lock_token, "lock-");
+
+      /* Append the encoded path, and we're done constructing the lock 
token */
+      svn_stringbuf_appendstr(lock_token, encoded_path);
+
+      /* For the sake of completeness */
+      SVN_ERR (svn_utf_cstring_to_utf8 (&pname_utf8, lock_token->data, 
subpool));
+
+      /* Lock tokens are stored in rev 0 */
+      svn_opt_revision_t rev0;
+      rev0.kind = svn_opt_revision_number;
+      rev0.value.number = 0;
+
+      /* Fetch the current property */
+      svn_error_t *err = svn_client_revprop_get (&pname_utf8[1], 
&cur_lock_owner,
+                                                URL, &rev0,
+                                                &rev, ctx, subpool);
+      if (err)
+        {
+          SVN_ERR(svn_cmdline_printf(subpool, _("Getting current lock owner 
failed: '%s'\n"), err->message));
+        }
+
+      /* Delete lock-token revprop. This will fail the in 
pre-revprop-change
+         hook if the resource is locked by another user. */
+      err = svn_client_revprop_set (pname_utf8,
+                                    /* must supply a value, or the hook 
won't be called */
+                                    svn_string_create( "ignore", subpool),
+                                    URL, &rev0,
+                                    &rev, TRUE    /* force*/,
+                                    ctx, subpool);
+
+      if (err)
+        {
+          if(!cur_lock_owner || !cur_lock_owner->data)
+            SVN_ERR(svn_cmdline_printf(subpool, _("Unable to obtain lock 
owner for %s (%s): %s\n"), target, pname_utf8, err->message));
+          else
+            SVN_ERR(svn_cmdline_printf(subpool, _("Could not unlock %s: The 
file locked by '%s'\n"), target, cur_lock_owner->data));
+        }
+      else
+        {
+          SVN_ERR(svn_cmdline_printf(subpool, _("Successfully unlocked 
%s\n"), target));
+        }
+
+  }/* for each target */
+
+  svn_pool_destroy (subpool);
+
+  return SVN_NO_ERROR;
+}



