/* This implements the `svn_opt_subcommand_t' interface. */
svn_error_t *
svn_cl__proplist(apr_getopt_t *os,
                 void *baton,
                 apr_pool_t *scratch_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;

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

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

  if (opt_state->revprop)  /* operate on revprops */
    {
      svn_revnum_t rev;
      const char *URL;
      apr_hash_t *proplist;

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

      /* Let libsvn_client do the real work. */
      SVN_ERR(svn_client_revprop_list(&proplist,
                                      URL, &(opt_state->start_revision),
                                      &rev, ctx, scratch_pool));

      if (opt_state->xml)
        {
          svn_stringbuf_t *sb = NULL;
          char *revstr = apr_psprintf(scratch_pool, "%ld", rev);

          SVN_ERR(svn_cl__xml_print_header("properties", scratch_pool));

          svn_xml_make_open_tag(&sb, scratch_pool, svn_xml_normal,
                                "revprops",
                                "rev", revstr, NULL);
          SVN_ERR(svn_cl__print_xml_prop_hash
                  (&sb, proplist, (! opt_state->verbose), scratch_pool));
          svn_xml_make_close_tag(&sb, scratch_pool, "revprops");

          SVN_ERR(svn_cl__error_checked_fputs(sb->data, stdout));
          SVN_ERR(svn_cl__xml_print_footer("properties", scratch_pool));
        }
      else
        {
          SVN_ERR
            (svn_cmdline_printf(scratch_pool,
                                _("Unversioned properties on revision %ld:\n"),
                                rev));

          SVN_ERR(svn_cl__print_prop_hash
                  (NULL, proplist, (! opt_state->verbose), scratch_pool));
        }
    }
  else  /* operate on normal, versioned properties (not revprops) */
    {
      int i;
      apr_pool_t *iterpool;
      svn_proplist_receiver_t pl_receiver;
      svn_boolean_t had_errors = FALSE;

      if (opt_state->xml)
        {
          SVN_ERR(svn_cl__xml_print_header("properties", scratch_pool));
          pl_receiver = proplist_receiver_xml;
        }
      else
        {
          pl_receiver = proplist_receiver;
        }

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

      iterpool = svn_pool_create(scratch_pool);
      for (i = 0; i < targets->nelts; i++)
        {
          const char *target = APR_ARRAY_IDX(targets, i, const char *);
          proplist_baton_t pl_baton;
          const char *truepath;
          svn_opt_revision_t peg_revision;
          svn_boolean_t success;

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

          pl_baton.is_url = svn_path_is_url(target);
          pl_baton.opt_state = opt_state;

          /* Check for a peg revision. */
          SVN_ERR(svn_opt_parse_path(&peg_revision, &truepath, target,
                                     iterpool));

          SVN_ERR(svn_cl__try(
                   svn_client_proplist3(truepath, &peg_revision,
                                        &(opt_state->start_revision),
                                        opt_state->depth,
                                        opt_state->changelists,
                                        pl_receiver, &pl_baton,
                                        ctx, iterpool),
                   &success, opt_state->quiet,
                   SVN_ERR_UNVERSIONED_RESOURCE,
                   SVN_ERR_ENTRY_NOT_FOUND,
                   SVN_NO_ERROR));

          if (!success)
            had_errors = TRUE;
        }
      svn_pool_destroy(iterpool);

      if (opt_state->xml)
        SVN_ERR(svn_cl__xml_print_footer("properties", scratch_pool));

      /* Error out *after* we closed the XML element */
      if (had_errors)
        return svn_error_create(SVN_ERR_ILLEGAL_TARGET, NULL,
                                _("Could not display info for all targets "
                                  "because some targets don't exist"));
    }

  return SVN_NO_ERROR;
}
Py::Object pysvn_client::cmd_proplist( const Py::Tuple &a_args, const Py::Dict &a_kws )
{
    static argument_description args_desc[] =
    {
        { true,  name_url_or_path },
        { false, name_revision },
        { false, name_recurse },
#if defined( PYSVN_HAS_CLIENT_PROPLIST2 )
        { false, name_peg_revision },
#endif
#if defined( PYSVN_HAS_CLIENT_PROPLIST3 )
        { false, name_depth },
        { false, name_changelists },
#endif
        { false, NULL }
    };
    FunctionArguments args( "proplist", args_desc, a_args, a_kws );
    args.check();

    Py::List path_list( toListOfStrings( args.getArg( name_url_or_path ) ) );

    SvnPool pool( m_context );

#if defined( PYSVN_HAS_CLIENT_PROPLIST3 )
    apr_array_header_t *changelists = NULL;

    if( args.hasArg( name_changelists ) )
    {
        changelists = arrayOfStringsFromListOfStrings( args.getArg( name_changelists ), pool );
    }

    svn_depth_t depth = args.getDepth( name_depth, name_recurse, svn_depth_files, svn_depth_empty );
#else
    bool recurse = args.getBoolean( name_recurse, false );
#endif
    bool is_revision_setup = false;
    bool is_url = false;

    svn_opt_revision_t revision_url;
    svn_opt_revision_t revision_file;
    if( args.hasArg( name_revision ) )
    {
        revision_url = args.getRevision( name_revision );
        revision_file = revision_url;
    }
    else
    {
        revision_url.kind = svn_opt_revision_head;
        revision_file.kind = svn_opt_revision_working;
    }

#if defined( PYSVN_HAS_CLIENT_PROPLIST2 )
    svn_opt_revision_t peg_revision_url;
    svn_opt_revision_t peg_revision_file;
    if( args.hasArg( name_peg_revision ) )
    {
        peg_revision_url = args.getRevision( name_peg_revision );
        peg_revision_file = peg_revision_url;
    }
    else
    {
        peg_revision_url = revision_url;
        peg_revision_file = revision_file;
    }
#endif

    Py::List list_of_proplists;

    for( Py::List::size_type i=0; i<path_list.length(); i++ )
    {
        Py::String path_str( asUtf8String( path_list[i] ) );
        std::string path( path_str.as_std_string() );
        std::string norm_path( svnNormalisedIfPath( path, pool ) );

        svn_opt_revision_t revision;
        svn_opt_revision_t peg_revision;
        if( !is_revision_setup )
            if( is_svn_url( path ) )
            {
                revision = revision_url;
#if defined( PYSVN_HAS_CLIENT_PROPLIST2 )
                peg_revision = peg_revision_url;
#endif
                is_url = true;
            }
            else
            {
                revision = revision_file;
#if defined( PYSVN_HAS_CLIENT_PROPLIST2 )
                peg_revision = peg_revision_file;
#endif
            }
        else if( is_svn_url( path ) && !is_url )
        {
            throw Py::AttributeError( "cannot mix URL and PATH in name_path" );
        }

        try
        {
            const char *norm_path_c_str= norm_path.c_str();
            checkThreadPermission();

            PythonAllowThreads permission( m_context );

#if defined( PYSVN_HAS_CLIENT_PROPLIST3 )
            ProplistReceiveBaton proplist_baton( &permission, pool, list_of_proplists );
            svn_error_t *error = svn_client_proplist3
                                 (
                                     norm_path_c_str,
                                     &peg_revision,
                                     &revision,
                                     depth,
                                     changelists,
                                     proplist_receiver_c,
                                     reinterpret_cast<void *>( &proplist_baton ),
                                     m_context,
                                     pool
                                 );
#elif defined( PYSVN_HAS_CLIENT_PROPLIST2 )
            apr_array_header_t *props = NULL;
            svn_error_t *error = svn_client_proplist2
                                 (
                                     &props,
                                     norm_path_c_str,
                                     &peg_revision,
                                     &revision,
                                     recurse,
                                     m_context,
                                     pool
                                 );
#else
            apr_array_header_t *props = NULL;
            svn_error_t *error = svn_client_proplist
                                 (
                                     &props,
                                     norm_path_c_str,
                                     &revision,
                                     recurse,
                                     m_context,
                                     pool
                                 );
#endif
            permission.allowThisThread();
            if( error != NULL )
                throw SvnException( error );

#if !defined( PYSVN_HAS_CLIENT_PROPLIST3 )
            proplistToObject( list_of_proplists, props, pool );
#endif
        }
        catch( SvnException &e )
        {
            // use callback error over ClientException
            m_context.checkForError( m_module.client_error );

            throw_client_error( e );
        }
    }

    return list_of_proplists;
}
static gpointer properties_thread (gpointer user_data)
{
  struct thread_args *args = user_data;
  svn_opt_revision_t revision;
  svn_error_t *err;
  svn_client_ctx_t *ctx = args->ctx;
  apr_pool_t *subpool, *pool = args->pool;
  TshPropertiesDialog *dialog = args->dialog;
  gchar *path = args->path;
  gchar *set_key = args->set_key;
  gchar *set_value = args->set_value;
  gboolean depth = args->depth;
  svn_string_t *value;
  GtkWidget *error;
  gchar *error_str;
#if CHECK_SVN_VERSION_G(1,7)
  apr_array_header_t *paths;
#endif

  args->set_key = NULL;
  args->set_value = NULL;

  subpool = svn_pool_create (pool);

  if (set_key)
  {
    value = set_value?svn_string_create(set_value, subpool):NULL;

#if CHECK_SVN_VERSION_G(1,7)
    paths = apr_array_make (subpool, 1, sizeof (const char *));
    APR_ARRAY_PUSH (paths, const char *) = path;

    if ((err = svn_client_propset_local(set_key, value, paths, depth, FALSE,
                                        NULL, ctx, subpool)))
#else
    if ((err = svn_client_propset3(NULL, set_key, value, path, depth, FALSE,
                                   SVN_INVALID_REVNUM, NULL, NULL, ctx,
                                   subpool)))
#endif
    {
      error_str = tsh_strerror(err);

G_GNUC_BEGIN_IGNORE_DEPRECATIONS
      gdk_threads_enter();
G_GNUC_END_IGNORE_DEPRECATIONS

      error = gtk_message_dialog_new(GTK_WINDOW(dialog), GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Set property failed"));
      gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(error), "%s", error_str);
      tsh_dialog_start(GTK_DIALOG(error), FALSE);

G_GNUC_BEGIN_IGNORE_DEPRECATIONS
      gdk_threads_leave();
G_GNUC_END_IGNORE_DEPRECATIONS

      g_free(error_str);

      svn_error_clear(err);
    }
  }

  g_free (set_key);
  g_free (set_value);

  revision.kind = svn_opt_revision_unspecified;


#if CHECK_SVN_VERSION_G(1,8)
  svn_proplist_receiver2_t receiver2;
  void *receiver2_baton;

  wrap_proplist_receiver(&receiver2, &receiver2_baton, tsh_proplist_func,
                         dialog, pool);

  if ((err = svn_client_proplist4(path, &revision, &revision, svn_depth_empty,
                                  NULL, FALSE, receiver2, receiver2_baton, ctx,
                                  subpool)))
#else
  if ((err = svn_client_proplist3(path, &revision, &revision, svn_depth_empty,
                                  NULL, tsh_proplist_func, dialog, ctx,
                                  subpool)))
#endif
  {
    svn_pool_destroy (subpool);

    error_str = tsh_strerror(err);

G_GNUC_BEGIN_IGNORE_DEPRECATIONS
    gdk_threads_enter();
G_GNUC_END_IGNORE_DEPRECATIONS

    tsh_properties_dialog_done (dialog);

    error = gtk_message_dialog_new(GTK_WINDOW(dialog), GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Properties failed"));
    gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(error), "%s", error_str);
    tsh_dialog_start(GTK_DIALOG(error), FALSE);

G_GNUC_BEGIN_IGNORE_DEPRECATIONS
    gdk_threads_leave();
G_GNUC_END_IGNORE_DEPRECATIONS

    g_free(error_str);

    svn_error_clear(err);
    tsh_reset_cancel();
    return GINT_TO_POINTER (FALSE);
  }

  svn_pool_destroy (subpool);

G_GNUC_BEGIN_IGNORE_DEPRECATIONS
  gdk_threads_enter();
  tsh_properties_dialog_done (dialog);
  gdk_threads_leave();
G_GNUC_END_IGNORE_DEPRECATIONS

  tsh_reset_cancel();
  return GINT_TO_POINTER (TRUE);
}