예제 #1
0
파일: move-cmd.c 프로젝트: aosm/subversion
/* This implements the `svn_opt_subcommand_t' interface. */
svn_error_t *
svn_cl__move(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_array_header_t *targets;
  const char *dst_path;
  svn_commit_info_t *commit_info = NULL;
  svn_error_t *err;

  SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os,
                                                      opt_state->targets,
                                                      ctx, pool));

  if (targets->nelts < 2)
    return svn_error_create(SVN_ERR_CL_INSUFFICIENT_ARGS, 0, NULL);

  if (opt_state->start_revision.kind != svn_opt_revision_unspecified
      && opt_state->start_revision.kind != svn_opt_revision_head)
    {
      return svn_error_create
        (SVN_ERR_UNSUPPORTED_FEATURE, NULL,
         _("Cannot specify revisions (except HEAD) with move operations"));
    }

  if (! opt_state->quiet)
    svn_cl__get_notifier(&ctx->notify_func2, &ctx->notify_baton2, FALSE,
                         FALSE, FALSE, pool);

  dst_path = APR_ARRAY_IDX(targets, targets->nelts - 1, const char *);
  apr_array_pop(targets);

  if (! svn_path_is_url(dst_path))
    {
      ctx->log_msg_func3 = NULL;
      if (opt_state->message || opt_state->filedata || opt_state->revprop_table)
        return svn_error_create
          (SVN_ERR_CL_UNNECESSARY_LOG_MESSAGE, NULL,
           _("Local, non-commit operations do not take a log message "
             "or revision properties"));
    }

  if (ctx->log_msg_func3)
    SVN_ERR(svn_cl__make_log_msg_baton(&(ctx->log_msg_baton3), opt_state,
                                       NULL, ctx->config, pool));

  SVN_ERR(svn_opt__eat_peg_revisions(&targets, targets, pool));

  err = svn_client_move5(&commit_info, targets, dst_path, opt_state->force,
                         TRUE, opt_state->parents, opt_state->revprop_table,
                         ctx, pool);

  if (err)
    err = svn_cl__may_need_force(err);

  if (ctx->log_msg_func3)
    SVN_ERR(svn_cl__cleanup_log_msg(ctx->log_msg_baton3, err, pool));
  else if (err)
    return err;

  if (commit_info && ! opt_state->quiet)
    SVN_ERR(svn_cl__print_commit_info(commit_info, pool));

  return SVN_NO_ERROR;
}
예제 #2
0
파일: copy-cmd.c 프로젝트: aosm/subversion
/* This implements the `svn_opt_subcommand_t' interface. */
svn_error_t *
svn_cl__copy(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_array_header_t *targets, *sources;
  const char *src_path, *dst_path;
  svn_boolean_t srcs_are_urls, dst_is_url;
  svn_commit_info_t *commit_info = NULL;
  svn_error_t *err;
  int i;

  SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os,
                                                      opt_state->targets,
                                                      ctx, pool));
  if (targets->nelts < 2)
    return svn_error_create(SVN_ERR_CL_INSUFFICIENT_ARGS, 0, NULL);

  /* Get the src list and associated peg revs */
  sources = apr_array_make(pool, targets->nelts - 1,
                           sizeof(svn_client_copy_source_t *));
  for (i = 0; i < (targets->nelts - 1); i++)
    {
      const char *target = APR_ARRAY_IDX(targets, i, const char *);
      svn_client_copy_source_t *source = apr_palloc(pool, sizeof(*source));
      const char *src;
      svn_opt_revision_t *peg_revision = apr_palloc(pool,
                                                    sizeof(*peg_revision));

      SVN_ERR(svn_opt_parse_path(peg_revision, &src, target, pool));
      source->path = src;
      source->revision = &(opt_state->start_revision);
      source->peg_revision = peg_revision;

      APR_ARRAY_PUSH(sources, svn_client_copy_source_t *) = source;
    }

  SVN_ERR(svn_opt__eat_peg_revisions(&targets, targets, pool));

  /* Figure out which type of trace editor to use.
     If the src_paths are not homogeneous, setup_copy will return an error. */
  src_path = APR_ARRAY_IDX(targets, 0, const char *);
  srcs_are_urls = svn_path_is_url(src_path);
  dst_path = APR_ARRAY_IDX(targets, targets->nelts - 1, const char *);
  apr_array_pop(targets);
  dst_is_url = svn_path_is_url(dst_path);

  if ((! srcs_are_urls) && (! dst_is_url))
    {
      /* WC->WC */
      if (! opt_state->quiet)
        svn_cl__get_notifier(&ctx->notify_func2, &ctx->notify_baton2,
                             FALSE, FALSE, FALSE, pool);
    }
  else if ((! srcs_are_urls) && (dst_is_url))
    {
      /* WC->URL : Use notification. */
      /* ### todo:

         We'd like to use the notifier, but we MAY have a couple of
         problems with that, the same problems that used to apply to
         the old trace_editor:

         1) We don't know where the commit editor for this case will
            be anchored with respect to the repository, so we can't
            use the DST_URL.

         2) While we do know where the commit editor will be driven
            from with respect to our working copy, we don't know what
            basenames will be chosen for our committed things.  So a
            copy of dir1/foo.c to http://.../dir2/foo-copy-c would
            display like: "Adding   dir1/foo-copy.c", which could be a
            bogus path.
      */
    }
  else if ((srcs_are_urls) && (! dst_is_url))
    {
      /* URL->WC : Use checkout-style notification. */
      if (! opt_state->quiet)
        svn_cl__get_notifier(&ctx->notify_func2, &ctx->notify_baton2, TRUE,
                             FALSE, FALSE, pool);
    }
  /* else URL -> URL, meaning that no notification is needed. */

  if (! dst_is_url)
    {
      ctx->log_msg_func3 = NULL;
      if (opt_state->message || opt_state->filedata || opt_state->revprop_table)
        return svn_error_create
          (SVN_ERR_CL_UNNECESSARY_LOG_MESSAGE, NULL,
           _("Local, non-commit operations do not take a log message "
             "or revision properties"));
    }

  if (ctx->log_msg_func3)
    SVN_ERR(svn_cl__make_log_msg_baton(&(ctx->log_msg_baton3), opt_state,
                                       NULL, ctx->config, pool));

  err = svn_client_copy5(&commit_info, sources, dst_path, TRUE,
                         opt_state->parents, opt_state->ignore_externals,
                         opt_state->revprop_table, ctx, pool);

  if (ctx->log_msg_func3)
    SVN_ERR(svn_cl__cleanup_log_msg(ctx->log_msg_baton3, err, pool));
  else if (err)
    return err;

  if (commit_info && ! opt_state->quiet)
    SVN_ERR(svn_cl__print_commit_info(commit_info, pool));

  return SVN_NO_ERROR;
}
예제 #3
0
/* This implements the `svn_opt_subcommand_t' interface. */
svn_error_t *
svn_cl__copy(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_array_header_t *targets, *sources;
  const char *src_path, *dst_path;
  svn_boolean_t srcs_are_urls, dst_is_url;
  svn_error_t *err;
  int i;

  SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os,
                                                      opt_state->targets,
                                                      ctx, FALSE, pool));
  if (targets->nelts < 2)
    return svn_error_create(SVN_ERR_CL_INSUFFICIENT_ARGS, 0, NULL);

  /* Get the src list and associated peg revs */
  sources = apr_array_make(pool, targets->nelts - 1,
                           sizeof(svn_client_copy_source_t *));
  for (i = 0; i < (targets->nelts - 1); i++)
    {
      const char *target = APR_ARRAY_IDX(targets, i, const char *);
      svn_client_copy_source_t *source = apr_palloc(pool, sizeof(*source));
      const char *src;
      svn_opt_revision_t *peg_revision = apr_palloc(pool,
                                                    sizeof(*peg_revision));

      SVN_ERR(svn_opt_parse_path(peg_revision, &src, target, pool));
      source->path = src;
      source->revision = &(opt_state->start_revision);
      source->peg_revision = peg_revision;

      APR_ARRAY_PUSH(sources, svn_client_copy_source_t *) = source;
    }

  SVN_ERR(svn_cl__eat_peg_revisions(&targets, targets, pool));

  /* Figure out which type of notification to use.
     (There is no need to check that the src paths are homogeneous;
     svn_client_copy6() through its subroutine try_copy() will return an
     error if they are not.) */
  src_path = APR_ARRAY_IDX(targets, 0, const char *);
  srcs_are_urls = svn_path_is_url(src_path);
  dst_path = APR_ARRAY_IDX(targets, targets->nelts - 1, const char *);
  apr_array_pop(targets);
  dst_is_url = svn_path_is_url(dst_path);

  if ((! srcs_are_urls) && (! dst_is_url))
    {
      /* WC->WC */
    }
  else if ((! srcs_are_urls) && (dst_is_url))
    {
      /* WC->URL : Use notification. */
      if (! opt_state->quiet)
        SVN_ERR(svn_cl__notifier_mark_wc_to_repos_copy(ctx->notify_baton2));
    }
  else if ((srcs_are_urls) && (! dst_is_url))
    {
     /* URL->WC : Use checkout-style notification. */
     if (! opt_state->quiet)
       SVN_ERR(svn_cl__notifier_mark_checkout(ctx->notify_baton2));
    }
  else
    {
      /* URL -> URL, meaning that no notification is needed. */
      ctx->notify_func2 = NULL;
    }

  if (! dst_is_url)
    {
      ctx->log_msg_func3 = NULL;
      if (opt_state->message || opt_state->filedata || opt_state->revprop_table)
        return svn_error_create
          (SVN_ERR_CL_UNNECESSARY_LOG_MESSAGE, NULL,
           _("Local, non-commit operations do not take a log message "
             "or revision properties"));
    }

  if (ctx->log_msg_func3)
    SVN_ERR(svn_cl__make_log_msg_baton(&(ctx->log_msg_baton3), opt_state,
                                       NULL, ctx->config, pool));

  err = svn_client_copy6(sources, dst_path, TRUE,
                         opt_state->parents, opt_state->ignore_externals,
                         opt_state->revprop_table,
                         svn_cl__print_commit_info, NULL,
                         ctx, pool);

  if (ctx->log_msg_func3)
    SVN_ERR(svn_cl__cleanup_log_msg(ctx->log_msg_baton3, err, pool));
  else if (err)
    return svn_error_trace(err);

  return SVN_NO_ERROR;
}
예제 #4
0
/* This implements the `svn_opt_subcommand_t' interface. */
svn_error_t *
svn_cl__commit(apr_getopt_t *os,
               void *baton,
               apr_pool_t *pool)
{
    svn_error_t *err;
    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_array_header_t *targets;
    apr_array_header_t *condensed_targets;
    const char *base_dir;
    svn_config_t *cfg;
    svn_boolean_t no_unlock = FALSE;
    struct copy_warning_notify_baton cwnb;

    SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os,
            opt_state->targets,
            ctx, FALSE, pool));

    SVN_ERR_W(svn_cl__check_targets_are_local_paths(targets),
              _("Commit targets must be local paths"));

    /* Add "." if user passed 0 arguments. */
    svn_opt_push_implicit_dot_target(targets, pool);

    SVN_ERR(svn_cl__eat_peg_revisions(&targets, targets, pool));

    /* Condense the targets (like commit does)... */
    SVN_ERR(svn_dirent_condense_targets(&base_dir, &condensed_targets, targets,
                                        TRUE, pool, pool));

    if ((! condensed_targets) || (! condensed_targets->nelts))
    {
        const char *parent_dir, *base_name;

        SVN_ERR(svn_wc_get_actual_target2(&parent_dir, &base_name, ctx->wc_ctx,
                                          base_dir, pool, pool));
        if (*base_name)
            base_dir = apr_pstrdup(pool, parent_dir);
    }

    if (opt_state->depth == svn_depth_unknown)
        opt_state->depth = svn_depth_infinity;

    cfg = apr_hash_get(ctx->config, SVN_CONFIG_CATEGORY_CONFIG,
                       APR_HASH_KEY_STRING);
    if (cfg)
        SVN_ERR(svn_config_get_bool(cfg, &no_unlock,
                                    SVN_CONFIG_SECTION_MISCELLANY,
                                    SVN_CONFIG_OPTION_NO_UNLOCK, FALSE));

    /* We're creating a new log message baton because we can use our base_dir
       to store the temp file, instead of the current working directory.  The
       client might not have write access to their working directory, but they
       better have write access to the directory they're committing.  */
    SVN_ERR(svn_cl__make_log_msg_baton(&(ctx->log_msg_baton3),
                                       opt_state, base_dir,
                                       ctx->config, pool));

    /* Copies are done server-side, and cheaply, which means they're
       effectively always done with infinite depth.  This is a potential
       cause of confusion for users trying to commit copied subtrees in
       part by restricting the commit's depth.  See issues #3699 and #3752. */
    if (opt_state->depth < svn_depth_infinity)
    {
        cwnb.wrapped_func = ctx->notify_func2;
        cwnb.wrapped_baton = ctx->notify_baton2;
        cwnb.depth = opt_state->depth;
        cwnb.warned = FALSE;
        ctx->notify_func2 = copy_warning_notify_func;
        ctx->notify_baton2 = &cwnb;
    }

    /* Commit. */
    err = svn_client_commit5(targets,
                             opt_state->depth,
                             no_unlock,
                             opt_state->keep_changelists,
                             TRUE /* commit_as_operations */,
                             opt_state->changelists,
                             opt_state->revprop_table,
                             (opt_state->quiet
                              ? NULL : svn_cl__print_commit_info),
                             NULL,
                             ctx,
                             pool);
    SVN_ERR(svn_cl__cleanup_log_msg(ctx->log_msg_baton3, err, pool));

    return SVN_NO_ERROR;
}
예제 #5
0
파일: copy-cmd.c 프로젝트: Alkzndr/freebsd
/* This implements the `svn_opt_subcommand_t' interface. */
svn_error_t *
svn_cl__copy(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_array_header_t *targets, *sources;
  const char *src_path, *dst_path;
  svn_boolean_t srcs_are_urls, dst_is_url;
  svn_error_t *err;
  int i;

  SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os,
                                                      opt_state->targets,
                                                      ctx, FALSE, pool));
  if (targets->nelts < 2)
    return svn_error_create(SVN_ERR_CL_INSUFFICIENT_ARGS, 0, NULL);

  /* Get the src list and associated peg revs */
  sources = apr_array_make(pool, targets->nelts - 1,
                           sizeof(svn_client_copy_source_t *));
  for (i = 0; i < (targets->nelts - 1); i++)
    {
      const char *target = APR_ARRAY_IDX(targets, i, const char *);
      svn_client_copy_source_t *source = apr_palloc(pool, sizeof(*source));
      const char *src;
      svn_opt_revision_t *peg_revision = apr_palloc(pool,
                                                    sizeof(*peg_revision));

      err = svn_opt_parse_path(peg_revision, &src, target, pool);

      if (err)
        {
          /* Issue #3606: 'svn cp .@HEAD target' gives
             svn: '@HEAD' is just a peg revision. Maybe try '@HEAD@' instead?

             This is caused by a first round of canonicalization in
             svn_cl__args_to_target_array_print_reserved(). Undo that in an
             attempt to fix this issue without revving many apis.
           */
          if (*target == '@' && err->apr_err == SVN_ERR_BAD_FILENAME)
            {
              svn_error_t *err2;

              err2 = svn_opt_parse_path(peg_revision, &src,
                                        apr_pstrcat(pool, ".", target,
                                                    (const char *)NULL), pool);

              if (err2)
                {
                  /* Fix attempt failed; return original error */
                  svn_error_clear(err2);
                }
              else
                {
                  /* Error resolved. Use path */
                  svn_error_clear(err);
                  err = NULL;
                }
            }

          if (err)
              return svn_error_trace(err);
        }

      source->path = src;
      source->revision = &(opt_state->start_revision);
      source->peg_revision = peg_revision;

      APR_ARRAY_PUSH(sources, svn_client_copy_source_t *) = source;
    }

  /* Get DST_PATH (the target path or URL) and check that no peg revision is
   * specified for it. */
  {
    const char *tgt = APR_ARRAY_IDX(targets, targets->nelts - 1, const char *);
    svn_opt_revision_t peg;

    SVN_ERR(svn_opt_parse_path(&peg, &dst_path, tgt, pool));
    if (peg.kind != svn_opt_revision_unspecified)
      return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
                               _("'%s': a peg revision is not allowed here"),
                               tgt);
  }

  /* Figure out which type of notification to use.
     (There is no need to check that the src paths are homogeneous;
     svn_client_copy6() through its subroutine try_copy() will return an
     error if they are not.) */
  src_path = APR_ARRAY_IDX(targets, 0, const char *);
  srcs_are_urls = svn_path_is_url(src_path);
  dst_is_url = svn_path_is_url(dst_path);

  if ((! srcs_are_urls) && (! dst_is_url))
    {
      /* WC->WC */
    }
  else if ((! srcs_are_urls) && (dst_is_url))
    {
      /* WC->URL : Use notification. */
      if (! opt_state->quiet)
        SVN_ERR(svn_cl__notifier_mark_wc_to_repos_copy(ctx->notify_baton2));
    }
  else if ((srcs_are_urls) && (! dst_is_url))
    {
     /* URL->WC : Use checkout-style notification. */
     if (! opt_state->quiet)
       SVN_ERR(svn_cl__notifier_mark_checkout(ctx->notify_baton2));
    }
  else
    {
      /* URL -> URL, meaning that no notification is needed. */
      ctx->notify_func2 = NULL;
    }

  if (! dst_is_url)
    {
      ctx->log_msg_func3 = NULL;
      if (opt_state->message || opt_state->filedata || opt_state->revprop_table)
        return svn_error_create
          (SVN_ERR_CL_UNNECESSARY_LOG_MESSAGE, NULL,
           _("Local, non-commit operations do not take a log message "
             "or revision properties"));
    }

  if (ctx->log_msg_func3)
    SVN_ERR(svn_cl__make_log_msg_baton(&(ctx->log_msg_baton3), opt_state,
                                       NULL, ctx->config, pool));

  err = svn_client_copy6(sources, dst_path, TRUE,
                         opt_state->parents, opt_state->ignore_externals,
                         opt_state->revprop_table,
                         (opt_state->quiet ? NULL : svn_cl__print_commit_info),
                         NULL,
                         ctx, pool);

  if (ctx->log_msg_func3)
    SVN_ERR(svn_cl__cleanup_log_msg(ctx->log_msg_baton3, err, pool));
  else if (err)
    return svn_error_trace(err);

  return SVN_NO_ERROR;
}
예제 #6
0
/* This implements the `svn_opt_subcommand_t' interface. */
svn_error_t *
svn_cl__propedit(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;
  const char *pname, *pname_utf8;
  apr_array_header_t *args, *targets;
  int i;

  /* Validate the input and get the property's name (and a UTF-8
     version of that name). */
  SVN_ERR(svn_opt_parse_num_args(&args, os, 1, pool));
  pname = APR_ARRAY_IDX(args, 0, const char *);
  SVN_ERR(svn_utf_cstring_to_utf8(&pname_utf8, pname, pool));
  if (! svn_prop_name_is_valid(pname_utf8))
    return svn_error_createf(SVN_ERR_CLIENT_PROPERTY_NAME, NULL,
                             _("'%s' is not a valid Subversion property name"),
                             pname_utf8);
  if (opt_state->encoding && !svn_prop_needs_translation(pname_utf8))
      return svn_error_create
          (SVN_ERR_UNSUPPORTED_FEATURE, NULL,
           _("--encoding option applies only to textual"
             " Subversion-controlled properties"));

  /* Suck up all the remaining arguments into a targets array */
  SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os,
                                                      opt_state->targets,
                                                      ctx, pool));

  if (opt_state->revprop)  /* operate on a revprop */
    {
      svn_revnum_t rev;
      const char *URL;
      svn_string_t *propval;
      svn_string_t original_propval;
      const char *temp_dir;

      /* Implicit "." is okay for revision properties; it just helps
         us find the right repository. */
      svn_opt_push_implicit_dot_target(targets, pool);

      SVN_ERR(svn_cl__revprop_prepare(&opt_state->start_revision, targets,
                                      &URL, pool));

      /* Fetch the current property. */
      SVN_ERR(svn_client_revprop_get(pname_utf8, &propval,
                                     URL, &(opt_state->start_revision),
                                     &rev, ctx, pool));

      if (! propval)
        {
          propval = svn_string_create("", pool);
          /* This is how we signify to svn_client_revprop_set2() that
             we want it to check that the original value hasn't
             changed, but that that original value was non-existent: */
          original_propval.data = NULL;  /* and .len is ignored */
        }
      else
        {
          original_propval = *propval;
        }

      /* Run the editor on a temporary file which contains the
         original property value... */
      SVN_ERR(svn_io_temp_dir(&temp_dir, pool));
      SVN_ERR(svn_cl__edit_string_externally
              (&propval, NULL,
               opt_state->editor_cmd, temp_dir,
               propval, "svn-prop",
               ctx->config,
               svn_prop_needs_translation(pname_utf8),
               opt_state->encoding, pool));

      /* ...and re-set the property's value accordingly. */
      if (propval)
        {
          SVN_ERR(svn_client_revprop_set2(pname_utf8,
                                          propval, &original_propval,
                                          URL, &(opt_state->start_revision),
                                          &rev, opt_state->force, ctx, pool));

          SVN_ERR
            (svn_cmdline_printf
             (pool,
              _("Set new value for property '%s' on revision %ld\n"),
              pname_utf8, rev));
        }
      else
        {
          SVN_ERR(svn_cmdline_printf
                  (pool, _("No changes to property '%s' on revision %ld\n"),
                   pname_utf8, rev));
        }
    }
  else if (opt_state->start_revision.kind != svn_opt_revision_unspecified)
    {
      return svn_error_createf
        (SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
         _("Cannot specify revision for editing versioned property '%s'"),
         pname_utf8);
    }
  else  /* operate on a normal, versioned property (not a revprop) */
    {
      apr_pool_t *subpool = svn_pool_create(pool);

      /* The customary implicit dot rule has been prone to user error
       * here.  For example, Jon Trowbridge <*****@*****.**> did
       *
       *    $ svn propedit HACKING
       *
       * and then when he closed his editor, he was surprised to see
       *
       *    Set new value for property 'HACKING' on ''
       *
       * ...meaning that the property named 'HACKING' had been set on
       * the current working directory, with the value taken from the
       * editor.  So we don't do the implicit dot thing anymore; an
       * explicit target is always required when editing a versioned
       * property.
       */
      if (targets->nelts == 0)
        {
          return svn_error_create
            (SVN_ERR_CL_INSUFFICIENT_ARGS, NULL,
             _("Explicit target argument required"));
        }

      SVN_ERR(svn_opt__eat_peg_revisions(&targets, targets, pool));

      /* For each target, edit the property PNAME. */
      for (i = 0; i < targets->nelts; i++)
        {
          apr_hash_t *props;
          const char *target = APR_ARRAY_IDX(targets, i, const char *);
          svn_string_t *propval, *edited_propval;
          const char *base_dir = target;
          const char *target_local;
          svn_wc_adm_access_t *adm_access;
          const svn_wc_entry_t *entry;
          svn_opt_revision_t peg_revision;
          svn_revnum_t base_rev = SVN_INVALID_REVNUM;

          svn_pool_clear(subpool);
          SVN_ERR(svn_cl__check_cancel(ctx->cancel_baton));

          /* Propedits can only happen on HEAD or the working copy, so
             the peg revision can be as unspecified. */
          peg_revision.kind = svn_opt_revision_unspecified;

          /* Fetch the current property. */
          SVN_ERR(svn_client_propget3(&props, pname_utf8, target,
                                      &peg_revision,
                                      &(opt_state->start_revision),
                                      &base_rev, svn_depth_empty,
                                      NULL, ctx, subpool));

          /* Get the property value. */
          propval = apr_hash_get(props, target, APR_HASH_KEY_STRING);
          if (! propval)
            propval = svn_string_create("", subpool);

          if (svn_path_is_url(target))
            {
              /* For URLs, put the temporary file in the current directory. */
              base_dir = ".";
            }
          else
            {
              if (opt_state->message || opt_state->filedata ||
                  opt_state->revprop_table)
                {
                  return svn_error_create
                    (SVN_ERR_CL_UNNECESSARY_LOG_MESSAGE, NULL,
                     _("Local, non-commit operations do not take a log message "
                       "or revision properties"));
                }

              /* Split the path if it is a file path. */
              SVN_ERR(svn_wc_adm_probe_open3(&adm_access, NULL, target,
                                             FALSE, 0, ctx->cancel_func,
                                             ctx->cancel_baton, subpool));
              SVN_ERR(svn_wc_entry(&entry, target, adm_access, FALSE, subpool));
              if (! entry)
                return svn_error_createf
                  (SVN_ERR_ENTRY_NOT_FOUND, NULL,
                   _("'%s' does not appear to be a working copy path"), target);
              if (entry->kind == svn_node_file)
                svn_path_split(target, &base_dir, NULL, subpool);
            }

          /* Run the editor on a temporary file which contains the
             original property value... */
          SVN_ERR(svn_cl__edit_string_externally(&edited_propval, NULL,
                                                 opt_state->editor_cmd,
                                                 base_dir,
                                                 propval,
                                                 "svn-prop",
                                                 ctx->config,
                                                 svn_prop_needs_translation
                                                 (pname_utf8),
                                                 opt_state->encoding,
                                                 subpool));

          target_local = svn_path_is_url(target) ? target
            : svn_path_local_style(target, subpool);

          /* ...and re-set the property's value accordingly. */
          if (edited_propval && !svn_string_compare(propval, edited_propval))
            {
              svn_commit_info_t *commit_info = NULL;
              svn_error_t *err = SVN_NO_ERROR;

              svn_cl__check_boolean_prop_val(pname_utf8, edited_propval->data,
                                             subpool);

              if (ctx->log_msg_func3)
                SVN_ERR(svn_cl__make_log_msg_baton(&(ctx->log_msg_baton3),
                                                   opt_state, NULL, ctx->config,
                                                   subpool));

              err = svn_client_propset3(&commit_info,
                                        pname_utf8, edited_propval, target,
                                        svn_depth_empty, opt_state->force,
                                        base_rev, NULL, opt_state->revprop_table,
                                        ctx, subpool);
              if (ctx->log_msg_func3)
                SVN_ERR(svn_cl__cleanup_log_msg(ctx->log_msg_baton3,
                                                err, pool));
              else if (err)
                return err;

              /* Print a message if we successfully committed or if it
                 was just a wc propset (but not if the user aborted an URL
                 propedit). */
              if (commit_info || ! svn_path_is_url(target))
                SVN_ERR
                  (svn_cmdline_printf
                   (subpool, _("Set new value for property '%s' on '%s'\n"),
                    pname_utf8, target_local));

              if (commit_info && ! opt_state->quiet)
                SVN_ERR(svn_cl__print_commit_info(commit_info, subpool));
            }
          else
            {
              SVN_ERR
                (svn_cmdline_printf
                 (subpool, _("No changes to property '%s' on '%s'\n"),
                  pname_utf8, target_local));
            }
        }
      svn_pool_destroy(subpool);
    }

  return SVN_NO_ERROR;
}