<commit_before>/*
 * Copyright © 2008 <NAME> <<EMAIL>>
 * -*- coding: utf-8 -*-
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
 */
#include <Python.h>
#include <apr_general.h>
#include <svn_wc.h>
#include <svn_path.h>
#include <svn_props.h>
#include <structmember.h>
#include <stdbool.h>
#include <apr_md5.h>
#include <apr_sha1.h>

#include "util.h"
#include "editor.h"
#include "wc.h"

/* Suppress warnings for this specific file, as it
 * provides backwards compatibility with svn < 1.7
 */
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"

static svn_wc_entry_callbacks2_t py_wc_entry_callbacks2;
static PyObject *py_entry(const svn_wc_entry_t *entry);

typedef struct {
    PyObject_VAR_HEAD
    svn_wc_adm_access_t *adm;
    apr_pool_t *pool;
} AdmObject;

typedef struct {
	PyObject_VAR_HEAD
	apr_pool_t *pool;
	svn_wc_entry_t entry;
} EntryObject;

svn_wc_adm_access_t *Adm_GetAdmAccess(PyObject *obj) {
    AdmObject *adm_obj = (AdmObject *)obj;
    return adm_obj->adm;
}

#define ADM_CHECK_CLOSED(adm_obj) \
    if (adm_obj->adm == NULL) { \
        PyErr_SetString(PyExc_RuntimeError, "WorkingCopy instance already closed"); \
        return NULL; \
    }

svn_wc_adm_access_t *PyObject_GetAdmAccess(PyObject *obj)
{
    return ((AdmObject *)obj)->adm;
}

static PyObject *adm_init(PyTypeObject *self, PyObject *args, PyObject *kwargs)
{
    PyObject *associated, *py_path;
    const char *path;
    bool write_lock=false;
    int depth=0;
    svn_wc_adm_access_t *parent_wc;
    svn_error_t *err;
    AdmObject *ret;
    char *kwnames[] = { "associated", "path", "write_lock", "depth", NULL };

    if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OO|bi", kwnames,
                                     &associated, &py_path, &write_lock, &depth))
        return NULL;

    ret = PyObject_New(AdmObject, &Adm_Type);
    if (ret == NULL)
        return NULL;

    ret->pool = Pool(NULL);
    if (ret->pool == NULL) {
        return NULL;
    }
    if (associated == Py_None) {
        parent_wc = NULL;
    } else {
        parent_wc = ((AdmObject *)associated)->adm;
    }

    path = py_object_to_svn_abspath(py_path, ret->pool);
    if (path == NULL) {
        Py_DECREF(ret);
        return NULL;
    }

    Py_BEGIN_ALLOW_THREADS
        err = svn_wc_adm_open3(&ret->adm, parent_wc,
                               path,
                               write_lock, depth, py_cancel_check, NULL,
                               ret->pool);
    Py_END_ALLOW_THREADS

    if (err != NULL) {
        handle_svn_error(err);
        svn_error_clear(err);
        Py_DECREF(ret);
        return NULL;
    }

    return (PyObject *)ret;
}

static PyObject *adm_access_path(PyObject *self)
{
    AdmObject *admobj = (AdmObject *)self;
    ADM_CHECK_CLOSED(admobj);
    return py_object_from_svn_abspath(svn_wc_adm_access_path(admobj->adm));
}

static PyObject *adm_locked(PyObject *self)
{
    AdmObject *admobj = (AdmObject *)self;
    ADM_CHECK_CLOSED(admobj);
    return PyBool_FromLong(svn_wc_adm_locked(admobj->adm));
}

static PyObject *adm_prop_get(PyObject *self, PyObject *args)
{
    char *name;
    const char *path;
    AdmObject *admobj = (AdmObject *)self;
    const svn_string_t *value;
    apr_pool_t *temp_pool;
    PyObject *ret, *py_path;

    if (!PyArg_ParseTuple(args, "sO", &name, &py_path))
        return NULL;

    ADM_CHECK_CLOSED(admobj);

    temp_pool = Pool(NULL);
    if (temp_pool == NULL) {
        return NULL;
    }

    path = py_object_to_svn_abspath(py_path, temp_pool);
    if (path == NULL) {
        apr_pool_destroy(temp_pool);
        return NULL;
    }

    RUN_SVN_WITH_POOL(temp_pool, svn_wc_prop_get(&value, name, path, admobj->adm, temp_pool));
    if (value == NULL || value->data == NULL) {
        ret = Py_None;
        Py_INCREF(ret);
    } else {
        ret = PyBytes_FromStringAndSize(value->data, value->len);
    }
    apr_pool_destroy(temp_pool);
    return ret;
}

static PyObject *adm_prop_set(PyObject *self, PyObject *args)
{
    char *name, *value;
    const char *path;
    AdmObject *admobj = (AdmObject *)self;
    bool skip_checks=false;
    apr_pool_t *temp_pool;
    int vallen;
    svn_string_t *cvalue;
    PyObject *py_path;
    PyObject *notify_func = Py_None;

    if (!PyArg_ParseTuple(args, "sz#O|bO", &name, &value, &vallen, &py_path, &skip_checks,
                          &notify_func))
        return NULL;

    ADM_CHECK_CLOSED(admobj);

    temp_pool = Pool(NULL);
    if (temp_pool == NULL) {
        return NULL;
    }

    path = py_object_to_svn_abspath(py_path, temp_pool);
    if (path == NULL) {
        apr_pool_destroy(temp_pool);
        return NULL;
    }

    if (value == NULL) {
        cvalue = NULL;
    } else {
        cvalue = svn_string_ncreate(value, vallen, temp_pool);
    }
#if ONLY_SINCE_SVN(1, 6)
    RUN_SVN_WITH_POOL(temp_pool, svn_wc_prop_set3(name, cvalue, path, admobj->adm,
                                                  skip_checks, py_wc_notify_func, (void *)notify_func,
                                                  temp_pool));
#else
    RUN_SVN_WITH_POOL(temp_pool, svn_wc_prop_set2(name, cvalue, path, admobj->adm,
                                                  skip_checks, temp_pool));
#endif
    apr_pool_destroy(temp_pool);

    Py_RETURN_NONE;
}

static PyObject *adm_entries_read(PyObject *self, PyObject *args)
{
    apr_hash_t *entries;
    AdmObject *admobj = (AdmObject *)self;
    apr_pool_t *temp_pool;
    bool show_hidden=false;
    apr_hash_index_t *idx;
    const char *key;
    apr_ssize_t klen;
    svn_wc_entry_t *entry;
    PyObject *py_entries, *obj;

    if (!PyArg_ParseTuple(args, "|b", &show_hidden))
        return NULL;

    ADM_CHECK_CLOSED(admobj);

    temp_pool = Pool(NULL);
    if (temp_pool == NULL)
        return NULL;
    RUN_SVN_WITH_POOL(temp_pool, svn_wc_entries_read(&entries, admobj->adm,
                                                     show_hidden, temp_pool));
    py_entries = PyDict_New();
    if (py_entries == NULL) {
        apr_pool_destroy(temp_pool);
        return NULL;
    }
    idx = apr_hash_first(temp_pool, entries);
    while (idx != NULL) {
        apr_hash_this(idx, (const void **)&key, &klen, (void **)&entry);
        if (entry == NULL) {
            obj = Py_None;
            Py_INCREF(obj);
        } else {
            obj = py_entry(entry);
        }
        PyDict_SetItemString(py_entries, key, obj);
        Py_DECREF(obj);
        idx = apr_hash_next(idx);
    }
    apr_pool_destroy(temp_pool);
    return py_entries;
}

static PyObject *adm_walk_entries(PyObject *self, PyObject *args)
{
    const char *path;
    PyObject *callbacks;
    bool show_hidden=false;
    apr_pool_t *temp_pool;
    AdmObject *admobj = (AdmObject *)self;
    svn_depth_t depth = svn_depth_infinity;
    PyObject *py_path;

    if (!PyArg_ParseTuple(args, "OO|bi", &py_path, &callbacks, &show_hidden, &depth))
        return NULL;

    ADM_CHECK_CLOSED(admobj);

    temp_pool = Pool(NULL);
    if (temp_pool == NULL) {
        return NULL;
    }

    path = py_object_to_svn_abspath(py_path, temp_pool);
    if (path == NULL) {
        apr_pool_destroy(temp_pool);
        return NULL;
    }

#if ONLY_SINCE_SVN(1, 5)
    RUN_SVN_WITH_POOL(temp_pool, svn_wc_walk_entries3(
                                                      path, admobj->adm,
                                                      &py_wc_entry_callbacks2, (void *)callbacks,
                                                      depth, show_hidden, py_cancel_check, NULL,
                                                      temp_pool));
#else
    if (depth != svn_depth_infinity) {
        PyErr_SetString(PyExc_NotImplementedError,
                        "depth != infinity not supported for svn < 1.5");
        apr_pool_destroy(temp_pool);
        return NULL;
    }
    RUN_SVN_WITH_POOL(temp_pool, svn_wc_walk_entries2(
                                                      path, admobj->adm,
                                                      &py_wc_entry_callbacks, (void *)callbacks,
                                                      show_hidden, py_cancel_check, NULL,
                                                      temp_pool));
#endif
    apr_pool_destroy(temp_pool);

    Py_RETURN_NONE;
}

static PyObject *adm_entry(PyObject *self, PyObject *args)
{
    const char *path;
    PyObject *py_path;
    bool show_hidden=false;
    apr_pool_t *temp_pool;
    AdmObject *admobj = (AdmObject *)self;
    const svn_wc_entry_t *entry;
    PyObject *ret;

    if (!PyArg_ParseTuple(args, "O|b", &py_path, &show_hidden))
        return NULL;

    ADM_CHECK_CLOSED(admobj);

    temp_pool = Pool(NULL);
    if (temp_pool == NULL) {
        return NULL;
    }

    path = py_object_to_svn_abspath(py_path, temp_pool);
    if (path == NULL) {
        apr_pool_destroy(temp_pool);
        return NULL;
    }

    RUN_SVN_WITH_POOL(temp_pool, svn_wc_entry(&entry, path, admobj->adm, show_hidden, temp_pool));

    if (entry == NULL) {
        PyErr_Format(PyExc_KeyError, "No such entry '%s'", path);
        ret = NULL;
    } else  {
        ret = py_entry(entry);
    }

    apr_pool_destroy(temp_pool);
    return ret;
}

static PyObject *adm_get_prop_diffs(PyObject *self, PyObject *args)
{
    const char *path;
    apr_pool_t *temp_pool;
    apr_array_header_t *propchanges;
    apr_hash_t *original_props;
    PyObject *py_path;
    AdmObject *admobj = (AdmObject *)self;
    PyObject *py_propchanges, *py_orig_props;

    if (!PyArg_ParseTuple(args, "O", &py_path))
        return NULL;

    ADM_CHECK_CLOSED(admobj);

    temp_pool = Pool(NULL);
    if (temp_pool == NULL) {
        return NULL;
    }

    path = py_object_to_svn_abspath(py_path, temp_pool);
    if (path == NULL) {
        apr_pool_destroy(temp_pool);
        return NULL;
    }
    RUN_SVN_WITH_POOL(temp_pool, svn_wc_get_prop_diffs(&propchanges, &original_props,
                                                       path, admobj->adm, temp_pool));
    py_propchanges = propchanges_to_list(propchanges);
    if (py_propchanges == NULL) {
        apr_pool_destroy(temp_pool);
        return NULL;
    }
    py_orig_props = prop_hash_to_dict(original_props);
    apr_pool_destroy(temp_pool);
    if (py_orig_props == NULL) {
        Py_DECREF(py_propchanges);
        return NULL;
    }
    return Py_BuildValue("(NN)", py_propchanges, py_orig_props);
}

static PyObject *adm_add(PyObject *self, PyObject *args, PyObject *kwargs)
{
    const char *path;
    const char *copyfrom_url = NULL;
    svn_revnum_t copyfrom_rev=-1;
    char *kwnames[] = { "path", "copyfrom_url", "copyfrom_rev", "notify_func", "depth", NULL };
    PyObject *notify_func=Py_None, *py_path;
    AdmObject *admobj = (AdmObject *)self;
    apr_pool_t *temp_pool;
    svn_depth_t depth = svn_depth_infinity;
    PyObject *py_copyfrom_url = Py_None;

    if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|OlOi", kwnames, &py_path,
                                     &py_copyfrom_url, &copyfrom_rev, &notify_func, &depth))
        return NULL;

    ADM_CHECK_CLOSED(admobj);

    temp_pool = Pool(NULL);
    if (temp_pool == NULL) {
        return NULL;
    }

    path = py_object_to_svn_abspath(py_path, temp_pool);
    if (path == NULL) {
        apr_pool_destroy(temp_pool);
        return NULL;
    }

    if (py_copyfrom_url != Py_None) {
        copyfrom_url = py_object_to_svn_uri(py_copyfrom_url, temp_pool);
        if (copyfrom_url == NULL) {
            apr_pool_destroy(temp_pool);
            return NULL;
        }
    } else {
        copyfrom_url = NULL;
    }

#if ONLY_SINCE_SVN(1, 6)
    RUN_SVN_WITH_POOL(temp_pool, svn_wc_add3(
                                             path, admobj->adm,
                                             depth, copyfrom_url,
                                             copyfrom_rev, py_cancel_check, NULL,
                                             py_wc_notify_func,
                                             (void *)notify_func,
                                             temp_pool));
#else
    if (depth != svn_depth_infinity) {
        PyErr_SetString(PyExc_NotImplementedError, "depth != infinity not supported on svn < 1.6");
        apr_pool_destroy(temp_pool);
        return NULL;
    }
    RUN_SVN_WITH_POOL(temp_pool, svn_wc_add2(
                                             path, admobj->adm, copyfrom_url,
                                             copyfrom_rev, py_cancel_check,
                                             NULL,
                                             py_wc_notify_func,
                                             (void *)notify_func,
                                             temp_pool));
#endif
    apr_pool_destroy(temp_pool);

    Py_RETURN_NONE;
}

static PyObject *adm_copy(PyObject *self, PyObject *args)
{
    AdmObject *admobj = (AdmObject *)self;
    char *src, *dst;
    PyObject *notify_func=Py_None;
    apr_pool_t *temp_pool;

    if (!PyArg_ParseTuple(args, "ss|O", &src, &dst, &notify_func))
        return NULL;

    ADM_CHECK_CLOSED(admobj);

    temp_pool = Pool(NULL);
    if (temp_pool == NULL)
        return NULL;
    RUN_SVN_WITH_POOL(temp_pool, svn_wc_copy2(src, admobj->adm, dst,
                                              py_cancel_check, NULL,
                                              py_wc_notify_func, (void *)notify_func,
                                              temp_pool));
    apr_pool_destroy(temp_pool);

    Py_RETURN_NONE;
}

static PyObject *adm_delete(PyObject *self, PyObject *args, PyObject *kwargs)
{
    AdmObject *admobj = (AdmObject *)self;
    apr_pool_t *temp_pool;
    char *kwnames[] = { "path", "notify_func", "keep_local",
        NULL };
    const char *path;
    PyObject *py_path;
    PyObject *notify_func=Py_None;
    bool keep_local = false;

    if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|Ob:delete", kwnames,
                                     &py_path, &notify_func, &keep_local))
        return NULL;

    ADM_CHECK_CLOSED(admobj);

    temp_pool = Pool(NULL);
    if (temp_pool == NULL) {
        return NULL;
    }

    path = py_object_to_svn_abspath(py_path, temp_pool);
    if (path == NULL) {
        apr_pool_destroy(temp_pool);
        return NULL;
    }

#if ONLY_SINCE_SVN(1, 5)
    RUN_SVN_WITH_POOL(temp_pool, svn_wc_delete3(path, admobj->adm,
                                                py_cancel_check, NULL,
                                                py_wc_notify_func, (void *)notify_func,
                                                keep_local,
                                                temp_pool));
#else
    if (keep_local) {
        PyErr_SetString(PyExc_NotImplementedError,
                        "keep_local not supported on Subversion < 1.5");
        return NULL;
    }

    RUN_SVN_WITH_POOL(temp_pool, svn_wc_delete2(path, admobj->adm,
                                                py_cancel_check, NULL,
                                                py_wc_notify_func, (void *)notify_func,
                                                temp_pool));
#endif
    apr_pool_destroy(temp_pool);

    Py_RETURN_NONE;
}

static PyObject *adm_crawl_revisions(PyObject *self, PyObject *args, PyObject *kwargs)
{
    const char *path;
    PyObject *reporter;
    bool restore_files=true, recurse=true, use_commit_times=true;
    PyObject *notify_func=Py_None;
    apr_pool_t *temp_pool;
    AdmObject *admobj = (AdmObject *)self;
    svn_wc_traversal_info_t *traversal_info;
    bool depth_compatibility_trick = false;
    bool honor_depth_exclude = false;
    char *kwnames[] = { "path", "reporter", "restore_files", "recurse",
        "use_commit_times", "notify_func", "depth_compatibility_trick",
        "honor_depth_exclude,", NULL };
    PyObject *py_path;

    if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OO|bbbObb", kwnames, &py_path,
                                     &reporter, &restore_files, &recurse, &use_commit_times,
                                     &notify_func, &depth_compatibility_trick, &honor_depth_exclude))
        return NULL;

    ADM_CHECK_CLOSED(admobj);

    temp_pool = Pool(NULL);
    if (temp_pool == NULL) {
        return NULL;
    }

    path = py_object_to_svn_abspath(py_path, temp_pool);
    if (path == NULL) {
        apr_pool_destroy(temp_pool);
        return NULL;
    }
    traversal_info = svn_wc_init_traversal_info(temp_pool);
#if ONLY_SINCE_SVN(1, 6)
    RUN_SVN_WITH_POOL(temp_pool, svn_wc_crawl_revisions4(path, admobj->adm,
                                                         &py_ra_reporter3, (void *)reporter,
                                                         restore_files, recurse?svn_depth_infinity:svn_depth_files,
                                                         honor_depth_exclude?TRUE:FALSE,
                                                         depth_compatibility_trick?TRUE:FALSE, use_commit_times,
                                                         py_wc_notify_func, (void *)notify_func,
                                                         traversal_info, temp_pool));
#elif ONLY_SINCE_SVN(1, 5)
    RUN_SVN_WITH_POOL(temp_pool, svn_wc_crawl_revisions3(path, admobj->adm,
                                                         &py_ra_reporter3, (void *)reporter,
                                                         restore_files, recurse?svn_depth_infinity:svn_depth_files,
                                                         depth_compatibility_trick, use_commit_times,
                                                         py_wc_notify_func, (void *)notify_func,
                                                         traversal_info, temp_pool));
#else
    RUN_SVN_WITH_POOL(temp_pool, svn_wc_crawl_revisions2(path, admobj->adm,
                                                         &py_ra_reporter2, (void *)reporter,
                                                         restore_files, recurse, use_commit_times,
                                                         py_wc_notify_func, (void *)notify_func,
                                                         traversal_info, temp_pool));
#endif
    apr_pool_destroy(temp_pool);

    Py_RETURN_NONE;
}

static void wc_done_handler(void *self)
{
    AdmObject *admobj = (AdmObject *)self;

    Py_DECREF(admobj);
}

static PyObject *adm_get_switch_editor(PyObject *self, PyObject *args, PyObject *kwargs)
{
    char *target;
    bool use_commit_times=true;
    PyObject * notify_func=Py_None;
    char *diff3_cmd=NULL;
    const svn_delta_editor_t *editor;
    AdmObject *admobj = (AdmObject *)self;
    void *edit_baton;
    apr_pool_t *pool;
    svn_revnum_t *latest_revnum;
    svn_error_t *err;
    bool allow_unver_obstructions = false;
    bool depth_is_sticky = false;
    int depth = svn_depth_infinity;
    const char *switch_url;
    PyObject *py_target, *py_switch_url;
    char *kwnames[] = {
        "target", "switch_url", "use_commit_times", "depth", "notify_func",
        "diff3_cmd", "depth_is_sticky", "allow_unver_obstructions", NULL };

    if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OO|biOzbb", kwnames,
                                     &py_target, &py_switch_url, &use_commit_times,
                                     &depth, &notify_func, &diff3_cmd,
                                     &depth_is_sticky,
                                     &allow_unver_obstructions))
        return NULL;

    ADM_CHECK_CLOSED(admobj);

    pool = Pool(NULL);
    if (pool == NULL)
        return NULL;

    target = py_object_to_svn_string(py_target, pool);
    if (target == NULL) {
        apr_pool_destroy(pool);
        return NULL;
    }

    switch_url = py_object_to_svn_uri(py_switch_url, pool);
    if (switch_url == NULL) {
        apr_pool_destroy(pool);
        return NULL;
    }

    latest_revnum = (svn_revnum_t *)apr_palloc(pool, sizeof(svn_revnum_t));
    Py_BEGIN_ALLOW_THREADS
#if ONLY_SINCE_SVN(1, 5)
        /* FIXME: Support fetch_func */
        /* FIXME: Support conflict func */
        err = svn_wc_get_switch_editor3(latest_revnum, admobj->adm, target, switch_url,
                                        use_commit_times, depth,
                                        depth_is_sticky?TRUE:FALSE, allow_unver_obstructions?TRUE:FALSE,
                                        py_wc_notify_func, (void *)notify_func,
                                        py_cancel_check, NULL,
                                        NULL, NULL, diff3_cmd, NULL, &editor,
                                        &edit_baton, NULL, pool);
#else
    if (allow_unver_obstructions) {
        PyErr_SetString(PyExc_NotImplementedError,
                        "allow_unver_obstructions is not supported in svn < 1.5");
        apr_pool_destroy(pool);
        PyEval_RestoreThread(_save);
        return NULL;
    }
    if (depth_is_sticky) {
        PyErr_SetString(PyExc_NotImplementedError,
                        "depth_is_sticky is not supported in svn < 1.5");
        apr_pool_destroy(pool);
        PyEval_RestoreThread(_save);
        return NULL;
    }
    err = svn_wc_get_switch_editor2(latest_revnum, admobj->adm, target, switch_url,
                                    use_commit_times, recurse, py_wc_notify_func, (void *)notify_func,
                                    py_cancel_check, NULL, diff3_cmd, &editor, &edit_baton,
                                    NULL, pool);
#endif
    Py_END_ALLOW_THREADS
        if (err != NULL) {
            handle_svn_error(err);
            svn_error_clear(err);
            apr_pool_destroy(pool);
            return NULL;
        }
    Py_INCREF(admobj);
    return new_editor_object(NULL, editor, edit_baton, pool, &Editor_Type,
                             wc_done_handler, admobj, NULL);
}

static PyObject *adm_get_update_editor(PyObject *self, PyObject *args, PyObject *kwargs)
{
    char *target;
    bool use_commit_times=true;
    PyObject * notify_func=Py_None;
    char *diff3_cmd=NULL;
    const svn_delta_editor_t *editor;
    AdmObject *admobj = (AdmObject *)self;
    void *edit_baton;
    apr_pool_t *pool;
    svn_revnum_t *latest_revnum;
    svn_error_t *err;
    bool allow_unver_obstructions = false;
    bool depth_is_sticky = false;
    int depth = svn_depth_infinity;
    PyObject *py_target;
    char *kwnames[] = {
        "target", "use_commit_times", "depth", "notify_func",
        "diff3_cmd", "depth_is_sticky", "allow_unver_obstructions", NULL };

    if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|biOzbb", kwnames,
                                     &py_target, &use_commit_times,
                                     &depth, &notify_func, &diff3_cmd,
                                     &depth_is_sticky,
                                     &allow_unver_obstructions))
        return NULL;

    ADM_CHECK_CLOSED(admobj);

    pool = Pool(NULL);
    if (pool == NULL)
        return NULL;

    target = py_object_to_svn_string(py_target, pool);
    if (target == NULL) {
        apr_pool_destroy(pool);
        return NULL;
    }
    latest_revnum = (svn_revnum_t *)apr_palloc(pool, sizeof(svn_revnum_t));
    Py_BEGIN_ALLOW_THREADS
#if ONLY_SINCE_SVN(1, 5)
        /* FIXME: Support fetch_func */
        /* FIXME: Support conflict func */
        err = svn_wc_get_update_editor3(latest_revnum, admobj->adm, target,
                                        use_commit_times, depth,
                                        depth_is_sticky?TRUE:FALSE, allow_unver_obstructions?TRUE:FALSE,
                                        py_wc_notify_func, (void *)notify_func,
                                        py_cancel_check, NULL,
                                        NULL, NULL, NULL, NULL,
                                        diff3_cmd, NULL, &editor, &edit_baton,
                                        NULL, pool);
#else
    if (allow_unver_obstructions) {
        PyErr_SetString(PyExc_NotImplementedError,
                        "allow_unver_obstructions is not supported in svn < 1.5");
        apr_pool_destroy(pool);
        PyEval_RestoreThread(_save);
        return NULL;
    }
    if (depth_is_sticky) {
        PyErr_SetString(PyExc_NotImplementedError,
                        "depth_is_sticky is not supported in svn < 1.5");
        apr_pool_destroy(pool);
        PyEval_RestoreThread(_save);
        return NULL;
    }
    err = svn_wc_get_update_editor2(latest_revnum, admobj->adm, target,
                                    use_commit_times, recurse, py_wc_notify_func, (void *)notify_func,
                                    py_cancel_check, NULL, diff3_cmd, &editor, &edit_baton,
                                    NULL, pool);
#endif
    Py_END_ALLOW_THREADS
        if (err != NULL) {
            handle_svn_error(err);
            svn_error_clear(err);
            apr_pool_destroy(pool);
            return NULL;
        }
    Py_INCREF(admobj);
    return new_editor_object(NULL, editor, edit_baton, pool, &Editor_Type,
                             wc_done_handler, admobj, NULL);
}

static PyObject *adm_has_binary_prop(PyObject *self, PyObject *args)
{
    const char *path;
    svn_boolean_t binary;
    AdmObject *admobj = (AdmObject *)self;
    apr_pool_t *temp_pool;
    PyObject *py_path;

    if (!PyArg_ParseTuple(args, "O", &py_path))
        return NULL;

    ADM_CHECK_CLOSED(admobj);

    temp_pool = Pool(NULL);
    if (temp_pool == NULL) {
        return NULL;
    }

    path = py_object_to_svn_abspath(py_path, temp_pool);
    if (path == NULL) {
        apr_pool_destroy(temp_pool);
        return NULL;
    }

    RUN_SVN_WITH_POOL(temp_pool, svn_wc_has_binary_prop(&binary, path, admobj->adm, temp_pool));

    apr_pool_destroy(temp_pool);

    return PyBool_FromLong(binary);
}

static PyObject *adm_process_committed(PyObject *self, PyObject *args, PyObject *kwargs)
{
    const char *path;
    char *rev_date = NULL, *rev_author = NULL;
    bool recurse, remove_lock = false;
    unsigned char *digest = NULL;
    svn_revnum_t new_revnum;
    PyObject *py_wcprop_changes = Py_None, *py_path;
    apr_array_header_t *wcprop_changes = NULL;
    AdmObject *admobj = (AdmObject *)self;
    apr_pool_t *temp_pool;
    int digest_len;
    bool remove_changelist = false;
    char *kwnames[] = { "path", "recurse", "new_revnum", "rev_date", "rev_author",
        "wcprop_changes", "remove_lock", "digest", "remove_changelist", NULL };

    if (!PyArg_ParseTupleAndKeywords(args, kwargs, "Oblzz|Obz#b", kwnames,
                                     &py_path, &recurse, &new_revnum, &rev_date,
                                     &rev_author, &py_wcprop_changes,
                                     &remove_lock, &digest, &digest_len, &remove_changelist))
        return NULL;

    PyErr_WarnEx(PyExc_DeprecationWarning, "process_committed is deprecated. Use process_committed_queue instead.", 2);

    ADM_CHECK_CLOSED(admobj);

    temp_pool = Pool(NULL);
    if (temp_pool == NULL) {
        return NULL;
    }

    path = py_object_to_svn_abspath(py_path, temp_pool);
    if (path == NULL) {
        apr_pool_destroy(temp_pool);
        return NULL;
    }

    if (!py_dict_to_wcprop_changes(py_wcprop_changes, temp_pool, &wcprop_changes)) {
        apr_pool_destroy(temp_pool);
        return NULL;
    }

#if ONLY_SINCE_SVN(1, 6)
    RUN_SVN_WITH_POOL(temp_pool, svn_wc_process_committed4(
                                                           path, admobj->adm, recurse, new_revnum,
                                                           rev_date, rev_author, wcprop_changes,
                                                           remove_lock, remove_changelist?TRUE:FALSE, digest, temp_pool));
#else
    if (remove_changelist) {
        PyErr_SetString(PyExc_NotImplementedError, "remove_changelist only supported in svn < 1.6");
        apr_pool_destroy(temp_pool);
        return NULL;
    }
    RUN_SVN_WITH_POOL(temp_pool, svn_wc_process_committed3(path, admobj->adm, recurse, new_revnum,
                                                           rev_date, rev_author, wcprop_changes,
                                                           remove_lock, digest, temp_pool));
#endif

    apr_pool_destroy(temp_pool);

    Py_RETURN_NONE;
}

static PyObject *adm_close(PyObject *self)
{
    AdmObject *admobj = (AdmObject *)self;
    if (admobj->adm != NULL) {
#if ONLY_SINCE_SVN(1, 6)
        apr_pool_t *temp_pool = Pool(NULL);
        Py_BEGIN_ALLOW_THREADS
            svn_wc_adm_close2(admobj->adm, temp_pool);
        apr_pool_destroy(temp_pool);
#else
        Py_BEGIN_ALLOW_THREADS
            svn_wc_adm_close(admobj->adm);
#endif
        Py_END_ALLOW_THREADS
            admobj->adm = NULL;
    }

    Py_RETURN_NONE;
}

static void adm_dealloc(PyObject *self)
{
    apr_pool_destroy(((AdmObject *)self)->pool);
    PyObject_Del(self);
}

static PyObject *adm_repr(PyObject *self)
{
    AdmObject *admobj = (AdmObject *)self;

    if (admobj->adm == NULL) {
        return PyRepr_FromFormat("<wc.WorkingCopy (closed) at 0x%p>", admobj);
    } else {
        return PyRepr_FromFormat("<wc.WorkingCopy at '%s'>",
                                 svn_wc_adm_access_path(admobj->adm));
    }
}

static PyObject *adm_remove_lock(PyObject *self, PyObject *args)
{
    const char *path;
    PyObject *py_path;
    AdmObject *admobj = (AdmObject *)self;
    apr_pool_t *temp_pool;

    if (!PyArg_ParseTuple(args, "O", &py_path))
        return NULL;

    ADM_CHECK_CLOSED(admobj);

    temp_pool = Pool(NULL);
    if (temp_pool == NULL) {
        return NULL;
    }

    path = py_object_to_svn_abspath(py_path, temp_pool);
    if (path == NULL) {
        apr_pool_destroy(temp_pool);
        return NULL;
    }

    RUN_SVN_WITH_POOL(temp_pool, svn_wc_remove_lock(path, admobj->adm, temp_pool))

        apr_pool_destroy(temp_pool);

    Py_RETURN_NONE;
}

static PyObject *get_ancestry(PyObject *self, PyObject *args)
{
    const char *path;
    char *url;
    svn_revnum_t rev;
    apr_pool_t *temp_pool;
    PyObject *py_path;
    AdmObject *admobj = (AdmObject *)self;

    if (!PyArg_ParseTuple(args, "O", &py_path))
        return NULL;

    ADM_CHECK_CLOSED(admobj);

    temp_pool = Pool(NULL);
    if (temp_pool == NULL) {
        return NULL;
    }

    path = py_object_to_svn_abspath(py_path, temp_pool);
    if (path == NULL) {
        apr_pool_destroy(temp_pool);
        return NULL;
    }

    RUN_SVN_WITH_POOL(temp_pool, svn_wc_get_ancestry(&url, &rev, path, admobj->adm, temp_pool));

    apr_pool_destroy(temp_pool);

    return Py_BuildValue("(si)", url, rev);
}

static PyObject *maybe_set_repos_root(PyObject *self, PyObject *args)
{
    char *path, *repos;
    apr_pool_t *temp_pool;
    AdmObject *admobj = (AdmObject *)self;

    if (!PyArg_ParseTuple(args, "ss", &path, &repos))
        return NULL;

    ADM_CHECK_CLOSED(admobj);

    temp_pool = Pool(NULL);
    if (temp_pool == NULL)
        return NULL;

    RUN_SVN_WITH_POOL(temp_pool, svn_wc_maybe_set_repos_root(admobj->adm, path, repos, temp_pool));

    Py_RETURN_NONE;
}

static PyObject *add_repos_file(PyObject *self, PyObject *args, PyObject *kwargs)
{
    char *kwnames[] = { "dst_path", "new_base_contents", "new_contents",
        "new_base_props", "new_props", "copyfrom_url", "copyfrom_rev",
        "notify", NULL };
    AdmObject *admobj = (AdmObject *)self;
    PyObject *py_dst_path;
    char *copyfrom_url = NULL;
    svn_revnum_t copyfrom_rev = -1;
    PyObject *py_new_base_contents, *py_new_contents, *py_new_base_props,
             *py_new_props, *notify = Py_None;
#if ONLY_SINCE_SVN(1, 6)
    apr_pool_t *temp_pool;
    const char *dst_path;
    svn_stream_t *new_contents, *new_base_contents;
    apr_hash_t *new_props, *new_base_props;
#endif

    if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OOOOO|zlO", kwnames,
                                     &py_dst_path, &py_new_base_contents, &py_new_contents, &py_new_base_props,
                                     &py_new_props, &copyfrom_url, &copyfrom_rev, &notify))
        return NULL;

    ADM_CHECK_CLOSED(admobj);

#if ONLY_SINCE_SVN(1, 6)
    temp_pool = Pool(NULL);
    if (temp_pool == NULL)
        return NULL;

    new_base_props = prop_dict_to_hash(temp_pool, py_new_base_props);

    new_props = prop_dict_to_hash(temp_pool, py_new_props);

    new_base_contents = new_py_stream(temp_pool, py_new_base_contents);

    new_contents = new_py_stream(temp_pool, py_new_contents);

    dst_path = py_object_to_svn_abspath(py_dst_path, temp_pool);

    RUN_SVN_WITH_POOL(temp_pool, svn_wc_add_repos_file3(dst_path, admobj->adm,
                                                        new_base_contents,
                                                        new_contents,
                                                        new_base_props,
                                                        new_props,
                                                        copyfrom_url, copyfrom_rev,
                                                        py_cancel_check, NULL,
                                                        py_wc_notify_func, notify, temp_pool));

    apr_pool_destroy(temp_pool);

    Py_RETURN_NONE;

#else
    PyErr_SetString(PyExc_NotImplementedError,
                    "add_repos_file3 not supported on svn < 1.6");
    return NULL;
#endif
}

static PyObject *mark_missing_deleted(PyObject *self, PyObject *args)
{
    const char *path;
    AdmObject *admobj = (AdmObject *)self;
    apr_pool_t *temp_pool;
    PyObject *py_path;

    if (!PyArg_ParseTuple(args, "O", &py_path))
        return NULL;

    ADM_CHECK_CLOSED(admobj);

    temp_pool = Pool(NULL);
    if (temp_pool == NULL) {
        return NULL;
    }

    path = py_object_to_svn_abspath(py_path, temp_pool);
    if (path == NULL) {
        apr_pool_destroy(temp_pool);
        return NULL;
    }

    RUN_SVN_WITH_POOL(temp_pool, svn_wc_mark_missing_deleted(path, admobj->adm, temp_pool));

    apr_pool_destroy(temp_pool);

    Py_RETURN_NONE;
}

static PyObject *remove_from_revision_control(PyObject *self, PyObject *args)
{
    char *name;
    bool destroy_wf = false, instant_error = false;
    AdmObject *admobj = (AdmObject *)self;
    apr_pool_t *temp_pool;

    if (!PyArg_ParseTuple(args, "s|bb", &name, &destroy_wf, &instant_error))
        return NULL;

    ADM_CHECK_CLOSED(admobj);

    temp_pool = Pool(NULL);
    if (temp_pool == NULL)
        return NULL;

    RUN_SVN_WITH_POOL(temp_pool,
                      svn_wc_remove_from_revision_control(admobj->adm, name,
                                                          destroy_wf?TRUE:FALSE, instant_error?TRUE:FALSE, py_cancel_check, NULL, temp_pool));

    apr_pool_destroy(temp_pool);

    Py_RETURN_NONE;
}

static PyObject *relocate(PyObject *self, PyObject *args)
{
    const char *path;
    char *from, *to;
    AdmObject *admobj = (AdmObject *)self;
    apr_pool_t *temp_pool;
    bool recurse = true;
    PyObject *py_validator = Py_None, *py_path;

    if (!PyArg_ParseTuple(args, "Oss|bO:relocate", &py_path, &from, &to, &recurse,
                          &py_validator))
        return NULL;

    ADM_CHECK_CLOSED(admobj);

    temp_pool = Pool(NULL);
    if (temp_pool == NULL) {
        return NULL;
    }

    path = py_object_to_svn_abspath(py_path, temp_pool);
    if (path == NULL) {
        apr_pool_destroy(temp_pool);
        return NULL;
    }

#if ONLY_SINCE_SVN(1, 6)
    RUN_SVN_WITH_POOL(temp_pool, svn_wc_relocate3(path, admobj->adm, from, to, recurse?TRUE:FALSE, wc_validator3, py_validator, temp_pool));
#else
    RUN_SVN_WITH_POOL(temp_pool, svn_wc_relocate2(path, admobj->adm, from, to, recurse?TRUE:FALSE, wc_validator2, py_validator, temp_pool));
#endif

    apr_pool_destroy(temp_pool);

    Py_RETURN_NONE;
}

static PyObject *crop_tree(PyObject *self, PyObject *args)
{
    char *target;
    svn_depth_t depth;
    PyObject *notify;
#if ONLY_SINCE_SVN(1, 6)
    apr_pool_t *temp_pool;
#endif
    AdmObject *admobj = (AdmObject *)self;

    if (!PyArg_ParseTuple(args, "si|O", &target, &depth, &notify))
        return NULL;

    ADM_CHECK_CLOSED(admobj);

#if ONLY_SINCE_SVN(1, 6)
    temp_pool = Pool(NULL);
    if (temp_pool == NULL)
        return NULL;

    RUN_SVN_WITH_POOL(temp_pool, svn_wc_crop_tree(admobj->adm,
                                                  target, depth, py_wc_notify_func, notify,
                                                  py_cancel_check, NULL, temp_pool));

    apr_pool_destroy(temp_pool);

    Py_RETURN_NONE;
#else
    PyErr_SetString(PyExc_NotImplementedError,
                    "crop_tree only available on subversion < 1.6");
    return NULL;
#endif
}

static PyObject *translated_stream(PyObject *self, PyObject *args)
{
    char *path, *versioned_file;
    StreamObject *ret;
    svn_stream_t *stream;
    AdmObject *admobj = (AdmObject *)self;
    apr_pool_t *stream_pool;
    PyObject *py_path, *py_versioned_file;
    int flags;

    if (!PyArg_ParseTuple(args, "OOi", &py_path, &py_versioned_file, &flags))
        return NULL;

#if ONLY_SINCE_SVN(1, 5)
    ADM_CHECK_CLOSED(admobj);

    stream_pool = Pool(NULL);
    if (stream_pool == NULL)
        return NULL;

    path = py_object_to_svn_abspath(py_path, stream_pool);
    if (path == NULL) {
        apr_pool_destroy(stream_pool);
        return NULL;
    }

    versioned_file = py_object_to_svn_abspath(py_versioned_file, stream_pool);
    if (versioned_file == NULL) {
        apr_pool_destroy(stream_pool);<commit_msg>Const.<commit_after>/*
 * Copyright © 2008 <NAME> <<EMAIL>>
 * -*- coding: utf-8 -*-
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
 */
#include <Python.h>
#include <apr_general.h>
#include <svn_wc.h>
#include <svn_path.h>
#include <svn_props.h>
#include <structmember.h>
#include <stdbool.h>
#include <apr_md5.h>
#include <apr_sha1.h>

#include "util.h"
#include "editor.h"
#include "wc.h"

/* Suppress warnings for this specific file, as it
 * provides backwards compatibility with svn < 1.7
 */
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"

static svn_wc_entry_callbacks2_t py_wc_entry_callbacks2;
static PyObject *py_entry(const svn_wc_entry_t *entry);

typedef struct {
    PyObject_VAR_HEAD
    svn_wc_adm_access_t *adm;
    apr_pool_t *pool;
} AdmObject;

typedef struct {
	PyObject_VAR_HEAD
	apr_pool_t *pool;
	svn_wc_entry_t entry;
} EntryObject;

svn_wc_adm_access_t *Adm_GetAdmAccess(PyObject *obj) {
    AdmObject *adm_obj = (AdmObject *)obj;
    return adm_obj->adm;
}

#define ADM_CHECK_CLOSED(adm_obj) \
    if (adm_obj->adm == NULL) { \
        PyErr_SetString(PyExc_RuntimeError, "WorkingCopy instance already closed"); \
        return NULL; \
    }

svn_wc_adm_access_t *PyObject_GetAdmAccess(PyObject *obj)
{
    return ((AdmObject *)obj)->adm;
}

static PyObject *adm_init(PyTypeObject *self, PyObject *args, PyObject *kwargs)
{
    PyObject *associated, *py_path;
    const char *path;
    bool write_lock=false;
    int depth=0;
    svn_wc_adm_access_t *parent_wc;
    svn_error_t *err;
    AdmObject *ret;
    char *kwnames[] = { "associated", "path", "write_lock", "depth", NULL };

    if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OO|bi", kwnames,
                                     &associated, &py_path, &write_lock, &depth))
        return NULL;

    ret = PyObject_New(AdmObject, &Adm_Type);
    if (ret == NULL)
        return NULL;

    ret->pool = Pool(NULL);
    if (ret->pool == NULL) {
        return NULL;
    }
    if (associated == Py_None) {
        parent_wc = NULL;
    } else {
        parent_wc = ((AdmObject *)associated)->adm;
    }

    path = py_object_to_svn_abspath(py_path, ret->pool);
    if (path == NULL) {
        Py_DECREF(ret);
        return NULL;
    }

    Py_BEGIN_ALLOW_THREADS
        err = svn_wc_adm_open3(&ret->adm, parent_wc,
                               path,
                               write_lock, depth, py_cancel_check, NULL,
                               ret->pool);
    Py_END_ALLOW_THREADS

    if (err != NULL) {
        handle_svn_error(err);
        svn_error_clear(err);
        Py_DECREF(ret);
        return NULL;
    }

    return (PyObject *)ret;
}

static PyObject *adm_access_path(PyObject *self)
{
    AdmObject *admobj = (AdmObject *)self;
    ADM_CHECK_CLOSED(admobj);
    return py_object_from_svn_abspath(svn_wc_adm_access_path(admobj->adm));
}

static PyObject *adm_locked(PyObject *self)
{
    AdmObject *admobj = (AdmObject *)self;
    ADM_CHECK_CLOSED(admobj);
    return PyBool_FromLong(svn_wc_adm_locked(admobj->adm));
}

static PyObject *adm_prop_get(PyObject *self, PyObject *args)
{
    char *name;
    const char *path;
    AdmObject *admobj = (AdmObject *)self;
    const svn_string_t *value;
    apr_pool_t *temp_pool;
    PyObject *ret, *py_path;

    if (!PyArg_ParseTuple(args, "sO", &name, &py_path))
        return NULL;

    ADM_CHECK_CLOSED(admobj);

    temp_pool = Pool(NULL);
    if (temp_pool == NULL) {
        return NULL;
    }

    path = py_object_to_svn_abspath(py_path, temp_pool);
    if (path == NULL) {
        apr_pool_destroy(temp_pool);
        return NULL;
    }

    RUN_SVN_WITH_POOL(temp_pool, svn_wc_prop_get(&value, name, path, admobj->adm, temp_pool));
    if (value == NULL || value->data == NULL) {
        ret = Py_None;
        Py_INCREF(ret);
    } else {
        ret = PyBytes_FromStringAndSize(value->data, value->len);
    }
    apr_pool_destroy(temp_pool);
    return ret;
}

static PyObject *adm_prop_set(PyObject *self, PyObject *args)
{
    char *name, *value;
    const char *path;
    AdmObject *admobj = (AdmObject *)self;
    bool skip_checks=false;
    apr_pool_t *temp_pool;
    int vallen;
    svn_string_t *cvalue;
    PyObject *py_path;
    PyObject *notify_func = Py_None;

    if (!PyArg_ParseTuple(args, "sz#O|bO", &name, &value, &vallen, &py_path, &skip_checks,
                          &notify_func))
        return NULL;

    ADM_CHECK_CLOSED(admobj);

    temp_pool = Pool(NULL);
    if (temp_pool == NULL) {
        return NULL;
    }

    path = py_object_to_svn_abspath(py_path, temp_pool);
    if (path == NULL) {
        apr_pool_destroy(temp_pool);
        return NULL;
    }

    if (value == NULL) {
        cvalue = NULL;
    } else {
        cvalue = svn_string_ncreate(value, vallen, temp_pool);
    }
#if ONLY_SINCE_SVN(1, 6)
    RUN_SVN_WITH_POOL(temp_pool, svn_wc_prop_set3(name, cvalue, path, admobj->adm,
                                                  skip_checks, py_wc_notify_func, (void *)notify_func,
                                                  temp_pool));
#else
    RUN_SVN_WITH_POOL(temp_pool, svn_wc_prop_set2(name, cvalue, path, admobj->adm,
                                                  skip_checks, temp_pool));
#endif
    apr_pool_destroy(temp_pool);

    Py_RETURN_NONE;
}

static PyObject *adm_entries_read(PyObject *self, PyObject *args)
{
    apr_hash_t *entries;
    AdmObject *admobj = (AdmObject *)self;
    apr_pool_t *temp_pool;
    bool show_hidden=false;
    apr_hash_index_t *idx;
    const char *key;
    apr_ssize_t klen;
    svn_wc_entry_t *entry;
    PyObject *py_entries, *obj;

    if (!PyArg_ParseTuple(args, "|b", &show_hidden))
        return NULL;

    ADM_CHECK_CLOSED(admobj);

    temp_pool = Pool(NULL);
    if (temp_pool == NULL)
        return NULL;
    RUN_SVN_WITH_POOL(temp_pool, svn_wc_entries_read(&entries, admobj->adm,
                                                     show_hidden, temp_pool));
    py_entries = PyDict_New();
    if (py_entries == NULL) {
        apr_pool_destroy(temp_pool);
        return NULL;
    }
    idx = apr_hash_first(temp_pool, entries);
    while (idx != NULL) {
        apr_hash_this(idx, (const void **)&key, &klen, (void **)&entry);
        if (entry == NULL) {
            obj = Py_None;
            Py_INCREF(obj);
        } else {
            obj = py_entry(entry);
        }
        PyDict_SetItemString(py_entries, key, obj);
        Py_DECREF(obj);
        idx = apr_hash_next(idx);
    }
    apr_pool_destroy(temp_pool);
    return py_entries;
}

static PyObject *adm_walk_entries(PyObject *self, PyObject *args)
{
    const char *path;
    PyObject *callbacks;
    bool show_hidden=false;
    apr_pool_t *temp_pool;
    AdmObject *admobj = (AdmObject *)self;
    svn_depth_t depth = svn_depth_infinity;
    PyObject *py_path;

    if (!PyArg_ParseTuple(args, "OO|bi", &py_path, &callbacks, &show_hidden, &depth))
        return NULL;

    ADM_CHECK_CLOSED(admobj);

    temp_pool = Pool(NULL);
    if (temp_pool == NULL) {
        return NULL;
    }

    path = py_object_to_svn_abspath(py_path, temp_pool);
    if (path == NULL) {
        apr_pool_destroy(temp_pool);
        return NULL;
    }

#if ONLY_SINCE_SVN(1, 5)
    RUN_SVN_WITH_POOL(temp_pool, svn_wc_walk_entries3(
                                                      path, admobj->adm,
                                                      &py_wc_entry_callbacks2, (void *)callbacks,
                                                      depth, show_hidden, py_cancel_check, NULL,
                                                      temp_pool));
#else
    if (depth != svn_depth_infinity) {
        PyErr_SetString(PyExc_NotImplementedError,
                        "depth != infinity not supported for svn < 1.5");
        apr_pool_destroy(temp_pool);
        return NULL;
    }
    RUN_SVN_WITH_POOL(temp_pool, svn_wc_walk_entries2(
                                                      path, admobj->adm,
                                                      &py_wc_entry_callbacks, (void *)callbacks,
                                                      show_hidden, py_cancel_check, NULL,
                                                      temp_pool));
#endif
    apr_pool_destroy(temp_pool);

    Py_RETURN_NONE;
}

static PyObject *adm_entry(PyObject *self, PyObject *args)
{
    const char *path;
    PyObject *py_path;
    bool show_hidden=false;
    apr_pool_t *temp_pool;
    AdmObject *admobj = (AdmObject *)self;
    const svn_wc_entry_t *entry;
    PyObject *ret;

    if (!PyArg_ParseTuple(args, "O|b", &py_path, &show_hidden))
        return NULL;

    ADM_CHECK_CLOSED(admobj);

    temp_pool = Pool(NULL);
    if (temp_pool == NULL) {
        return NULL;
    }

    path = py_object_to_svn_abspath(py_path, temp_pool);
    if (path == NULL) {
        apr_pool_destroy(temp_pool);
        return NULL;
    }

    RUN_SVN_WITH_POOL(temp_pool, svn_wc_entry(&entry, path, admobj->adm, show_hidden, temp_pool));

    if (entry == NULL) {
        PyErr_Format(PyExc_KeyError, "No such entry '%s'", path);
        ret = NULL;
    } else  {
        ret = py_entry(entry);
    }

    apr_pool_destroy(temp_pool);
    return ret;
}

static PyObject *adm_get_prop_diffs(PyObject *self, PyObject *args)
{
    const char *path;
    apr_pool_t *temp_pool;
    apr_array_header_t *propchanges;
    apr_hash_t *original_props;
    PyObject *py_path;
    AdmObject *admobj = (AdmObject *)self;
    PyObject *py_propchanges, *py_orig_props;

    if (!PyArg_ParseTuple(args, "O", &py_path))
        return NULL;

    ADM_CHECK_CLOSED(admobj);

    temp_pool = Pool(NULL);
    if (temp_pool == NULL) {
        return NULL;
    }

    path = py_object_to_svn_abspath(py_path, temp_pool);
    if (path == NULL) {
        apr_pool_destroy(temp_pool);
        return NULL;
    }
    RUN_SVN_WITH_POOL(temp_pool, svn_wc_get_prop_diffs(&propchanges, &original_props,
                                                       path, admobj->adm, temp_pool));
    py_propchanges = propchanges_to_list(propchanges);
    if (py_propchanges == NULL) {
        apr_pool_destroy(temp_pool);
        return NULL;
    }
    py_orig_props = prop_hash_to_dict(original_props);
    apr_pool_destroy(temp_pool);
    if (py_orig_props == NULL) {
        Py_DECREF(py_propchanges);
        return NULL;
    }
    return Py_BuildValue("(NN)", py_propchanges, py_orig_props);
}

static PyObject *adm_add(PyObject *self, PyObject *args, PyObject *kwargs)
{
    const char *path;
    const char *copyfrom_url = NULL;
    svn_revnum_t copyfrom_rev=-1;
    char *kwnames[] = { "path", "copyfrom_url", "copyfrom_rev", "notify_func", "depth", NULL };
    PyObject *notify_func=Py_None, *py_path;
    AdmObject *admobj = (AdmObject *)self;
    apr_pool_t *temp_pool;
    svn_depth_t depth = svn_depth_infinity;
    PyObject *py_copyfrom_url = Py_None;

    if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|OlOi", kwnames, &py_path,
                                     &py_copyfrom_url, &copyfrom_rev, &notify_func, &depth))
        return NULL;

    ADM_CHECK_CLOSED(admobj);

    temp_pool = Pool(NULL);
    if (temp_pool == NULL) {
        return NULL;
    }

    path = py_object_to_svn_abspath(py_path, temp_pool);
    if (path == NULL) {
        apr_pool_destroy(temp_pool);
        return NULL;
    }

    if (py_copyfrom_url != Py_None) {
        copyfrom_url = py_object_to_svn_uri(py_copyfrom_url, temp_pool);
        if (copyfrom_url == NULL) {
            apr_pool_destroy(temp_pool);
            return NULL;
        }
    } else {
        copyfrom_url = NULL;
    }

#if ONLY_SINCE_SVN(1, 6)
    RUN_SVN_WITH_POOL(temp_pool, svn_wc_add3(
                                             path, admobj->adm,
                                             depth, copyfrom_url,
                                             copyfrom_rev, py_cancel_check, NULL,
                                             py_wc_notify_func,
                                             (void *)notify_func,
                                             temp_pool));
#else
    if (depth != svn_depth_infinity) {
        PyErr_SetString(PyExc_NotImplementedError, "depth != infinity not supported on svn < 1.6");
        apr_pool_destroy(temp_pool);
        return NULL;
    }
    RUN_SVN_WITH_POOL(temp_pool, svn_wc_add2(
                                             path, admobj->adm, copyfrom_url,
                                             copyfrom_rev, py_cancel_check,
                                             NULL,
                                             py_wc_notify_func,
                                             (void *)notify_func,
                                             temp_pool));
#endif
    apr_pool_destroy(temp_pool);

    Py_RETURN_NONE;
}

static PyObject *adm_copy(PyObject *self, PyObject *args)
{
    AdmObject *admobj = (AdmObject *)self;
    char *src, *dst;
    PyObject *notify_func=Py_None;
    apr_pool_t *temp_pool;

    if (!PyArg_ParseTuple(args, "ss|O", &src, &dst, &notify_func))
        return NULL;

    ADM_CHECK_CLOSED(admobj);

    temp_pool = Pool(NULL);
    if (temp_pool == NULL)
        return NULL;
    RUN_SVN_WITH_POOL(temp_pool, svn_wc_copy2(src, admobj->adm, dst,
                                              py_cancel_check, NULL,
                                              py_wc_notify_func, (void *)notify_func,
                                              temp_pool));
    apr_pool_destroy(temp_pool);

    Py_RETURN_NONE;
}

static PyObject *adm_delete(PyObject *self, PyObject *args, PyObject *kwargs)
{
    AdmObject *admobj = (AdmObject *)self;
    apr_pool_t *temp_pool;
    char *kwnames[] = { "path", "notify_func", "keep_local",
        NULL };
    const char *path;
    PyObject *py_path;
    PyObject *notify_func=Py_None;
    bool keep_local = false;

    if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|Ob:delete", kwnames,
                                     &py_path, &notify_func, &keep_local))
        return NULL;

    ADM_CHECK_CLOSED(admobj);

    temp_pool = Pool(NULL);
    if (temp_pool == NULL) {
        return NULL;
    }

    path = py_object_to_svn_abspath(py_path, temp_pool);
    if (path == NULL) {
        apr_pool_destroy(temp_pool);
        return NULL;
    }

#if ONLY_SINCE_SVN(1, 5)
    RUN_SVN_WITH_POOL(temp_pool, svn_wc_delete3(path, admobj->adm,
                                                py_cancel_check, NULL,
                                                py_wc_notify_func, (void *)notify_func,
                                                keep_local,
                                                temp_pool));
#else
    if (keep_local) {
        PyErr_SetString(PyExc_NotImplementedError,
                        "keep_local not supported on Subversion < 1.5");
        return NULL;
    }

    RUN_SVN_WITH_POOL(temp_pool, svn_wc_delete2(path, admobj->adm,
                                                py_cancel_check, NULL,
                                                py_wc_notify_func, (void *)notify_func,
                                                temp_pool));
#endif
    apr_pool_destroy(temp_pool);

    Py_RETURN_NONE;
}

static PyObject *adm_crawl_revisions(PyObject *self, PyObject *args, PyObject *kwargs)
{
    const char *path;
    PyObject *reporter;
    bool restore_files=true, recurse=true, use_commit_times=true;
    PyObject *notify_func=Py_None;
    apr_pool_t *temp_pool;
    AdmObject *admobj = (AdmObject *)self;
    svn_wc_traversal_info_t *traversal_info;
    bool depth_compatibility_trick = false;
    bool honor_depth_exclude = false;
    char *kwnames[] = { "path", "reporter", "restore_files", "recurse",
        "use_commit_times", "notify_func", "depth_compatibility_trick",
        "honor_depth_exclude,", NULL };
    PyObject *py_path;

    if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OO|bbbObb", kwnames, &py_path,
                                     &reporter, &restore_files, &recurse, &use_commit_times,
                                     &notify_func, &depth_compatibility_trick, &honor_depth_exclude))
        return NULL;

    ADM_CHECK_CLOSED(admobj);

    temp_pool = Pool(NULL);
    if (temp_pool == NULL) {
        return NULL;
    }

    path = py_object_to_svn_abspath(py_path, temp_pool);
    if (path == NULL) {
        apr_pool_destroy(temp_pool);
        return NULL;
    }
    traversal_info = svn_wc_init_traversal_info(temp_pool);
#if ONLY_SINCE_SVN(1, 6)
    RUN_SVN_WITH_POOL(temp_pool, svn_wc_crawl_revisions4(path, admobj->adm,
                                                         &py_ra_reporter3, (void *)reporter,
                                                         restore_files, recurse?svn_depth_infinity:svn_depth_files,
                                                         honor_depth_exclude?TRUE:FALSE,
                                                         depth_compatibility_trick?TRUE:FALSE, use_commit_times,
                                                         py_wc_notify_func, (void *)notify_func,
                                                         traversal_info, temp_pool));
#elif ONLY_SINCE_SVN(1, 5)
    RUN_SVN_WITH_POOL(temp_pool, svn_wc_crawl_revisions3(path, admobj->adm,
                                                         &py_ra_reporter3, (void *)reporter,
                                                         restore_files, recurse?svn_depth_infinity:svn_depth_files,
                                                         depth_compatibility_trick, use_commit_times,
                                                         py_wc_notify_func, (void *)notify_func,
                                                         traversal_info, temp_pool));
#else
    RUN_SVN_WITH_POOL(temp_pool, svn_wc_crawl_revisions2(path, admobj->adm,
                                                         &py_ra_reporter2, (void *)reporter,
                                                         restore_files, recurse, use_commit_times,
                                                         py_wc_notify_func, (void *)notify_func,
                                                         traversal_info, temp_pool));
#endif
    apr_pool_destroy(temp_pool);

    Py_RETURN_NONE;
}

static void wc_done_handler(void *self)
{
    AdmObject *admobj = (AdmObject *)self;

    Py_DECREF(admobj);
}

static PyObject *adm_get_switch_editor(PyObject *self, PyObject *args, PyObject *kwargs)
{
    char *target;
    bool use_commit_times=true;
    PyObject * notify_func=Py_None;
    char *diff3_cmd=NULL;
    const svn_delta_editor_t *editor;
    AdmObject *admobj = (AdmObject *)self;
    void *edit_baton;
    apr_pool_t *pool;
    svn_revnum_t *latest_revnum;
    svn_error_t *err;
    bool allow_unver_obstructions = false;
    bool depth_is_sticky = false;
    int depth = svn_depth_infinity;
    const char *switch_url;
    PyObject *py_target, *py_switch_url;
    char *kwnames[] = {
        "target", "switch_url", "use_commit_times", "depth", "notify_func",
        "diff3_cmd", "depth_is_sticky", "allow_unver_obstructions", NULL };

    if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OO|biOzbb", kwnames,
                                     &py_target, &py_switch_url, &use_commit_times,
                                     &depth, &notify_func, &diff3_cmd,
                                     &depth_is_sticky,
                                     &allow_unver_obstructions))
        return NULL;

    ADM_CHECK_CLOSED(admobj);

    pool = Pool(NULL);
    if (pool == NULL)
        return NULL;

    target = py_object_to_svn_string(py_target, pool);
    if (target == NULL) {
        apr_pool_destroy(pool);
        return NULL;
    }

    switch_url = py_object_to_svn_uri(py_switch_url, pool);
    if (switch_url == NULL) {
        apr_pool_destroy(pool);
        return NULL;
    }

    latest_revnum = (svn_revnum_t *)apr_palloc(pool, sizeof(svn_revnum_t));
    Py_BEGIN_ALLOW_THREADS
#if ONLY_SINCE_SVN(1, 5)
        /* FIXME: Support fetch_func */
        /* FIXME: Support conflict func */
        err = svn_wc_get_switch_editor3(latest_revnum, admobj->adm, target, switch_url,
                                        use_commit_times, depth,
                                        depth_is_sticky?TRUE:FALSE, allow_unver_obstructions?TRUE:FALSE,
                                        py_wc_notify_func, (void *)notify_func,
                                        py_cancel_check, NULL,
                                        NULL, NULL, diff3_cmd, NULL, &editor,
                                        &edit_baton, NULL, pool);
#else
    if (allow_unver_obstructions) {
        PyErr_SetString(PyExc_NotImplementedError,
                        "allow_unver_obstructions is not supported in svn < 1.5");
        apr_pool_destroy(pool);
        PyEval_RestoreThread(_save);
        return NULL;
    }
    if (depth_is_sticky) {
        PyErr_SetString(PyExc_NotImplementedError,
                        "depth_is_sticky is not supported in svn < 1.5");
        apr_pool_destroy(pool);
        PyEval_RestoreThread(_save);
        return NULL;
    }
    err = svn_wc_get_switch_editor2(latest_revnum, admobj->adm, target, switch_url,
                                    use_commit_times, recurse, py_wc_notify_func, (void *)notify_func,
                                    py_cancel_check, NULL, diff3_cmd, &editor, &edit_baton,
                                    NULL, pool);
#endif
    Py_END_ALLOW_THREADS
        if (err != NULL) {
            handle_svn_error(err);
            svn_error_clear(err);
            apr_pool_destroy(pool);
            return NULL;
        }
    Py_INCREF(admobj);
    return new_editor_object(NULL, editor, edit_baton, pool, &Editor_Type,
                             wc_done_handler, admobj, NULL);
}

static PyObject *adm_get_update_editor(PyObject *self, PyObject *args, PyObject *kwargs)
{
    char *target;
    bool use_commit_times=true;
    PyObject * notify_func=Py_None;
    char *diff3_cmd=NULL;
    const svn_delta_editor_t *editor;
    AdmObject *admobj = (AdmObject *)self;
    void *edit_baton;
    apr_pool_t *pool;
    svn_revnum_t *latest_revnum;
    svn_error_t *err;
    bool allow_unver_obstructions = false;
    bool depth_is_sticky = false;
    int depth = svn_depth_infinity;
    PyObject *py_target;
    char *kwnames[] = {
        "target", "use_commit_times", "depth", "notify_func",
        "diff3_cmd", "depth_is_sticky", "allow_unver_obstructions", NULL };

    if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|biOzbb", kwnames,
                                     &py_target, &use_commit_times,
                                     &depth, &notify_func, &diff3_cmd,
                                     &depth_is_sticky,
                                     &allow_unver_obstructions))
        return NULL;

    ADM_CHECK_CLOSED(admobj);

    pool = Pool(NULL);
    if (pool == NULL)
        return NULL;

    target = py_object_to_svn_string(py_target, pool);
    if (target == NULL) {
        apr_pool_destroy(pool);
        return NULL;
    }
    latest_revnum = (svn_revnum_t *)apr_palloc(pool, sizeof(svn_revnum_t));
    Py_BEGIN_ALLOW_THREADS
#if ONLY_SINCE_SVN(1, 5)
        /* FIXME: Support fetch_func */
        /* FIXME: Support conflict func */
        err = svn_wc_get_update_editor3(latest_revnum, admobj->adm, target,
                                        use_commit_times, depth,
                                        depth_is_sticky?TRUE:FALSE, allow_unver_obstructions?TRUE:FALSE,
                                        py_wc_notify_func, (void *)notify_func,
                                        py_cancel_check, NULL,
                                        NULL, NULL, NULL, NULL,
                                        diff3_cmd, NULL, &editor, &edit_baton,
                                        NULL, pool);
#else
    if (allow_unver_obstructions) {
        PyErr_SetString(PyExc_NotImplementedError,
                        "allow_unver_obstructions is not supported in svn < 1.5");
        apr_pool_destroy(pool);
        PyEval_RestoreThread(_save);
        return NULL;
    }
    if (depth_is_sticky) {
        PyErr_SetString(PyExc_NotImplementedError,
                        "depth_is_sticky is not supported in svn < 1.5");
        apr_pool_destroy(pool);
        PyEval_RestoreThread(_save);
        return NULL;
    }
    err = svn_wc_get_update_editor2(latest_revnum, admobj->adm, target,
                                    use_commit_times, recurse, py_wc_notify_func, (void *)notify_func,
                                    py_cancel_check, NULL, diff3_cmd, &editor, &edit_baton,
                                    NULL, pool);
#endif
    Py_END_ALLOW_THREADS
        if (err != NULL) {
            handle_svn_error(err);
            svn_error_clear(err);
            apr_pool_destroy(pool);
            return NULL;
        }
    Py_INCREF(admobj);
    return new_editor_object(NULL, editor, edit_baton, pool, &Editor_Type,
                             wc_done_handler, admobj, NULL);
}

static PyObject *adm_has_binary_prop(PyObject *self, PyObject *args)
{
    const char *path;
    svn_boolean_t binary;
    AdmObject *admobj = (AdmObject *)self;
    apr_pool_t *temp_pool;
    PyObject *py_path;

    if (!PyArg_ParseTuple(args, "O", &py_path))
        return NULL;

    ADM_CHECK_CLOSED(admobj);

    temp_pool = Pool(NULL);
    if (temp_pool == NULL) {
        return NULL;
    }

    path = py_object_to_svn_abspath(py_path, temp_pool);
    if (path == NULL) {
        apr_pool_destroy(temp_pool);
        return NULL;
    }

    RUN_SVN_WITH_POOL(temp_pool, svn_wc_has_binary_prop(&binary, path, admobj->adm, temp_pool));

    apr_pool_destroy(temp_pool);

    return PyBool_FromLong(binary);
}

static PyObject *adm_process_committed(PyObject *self, PyObject *args, PyObject *kwargs)
{
    const char *path;
    char *rev_date = NULL, *rev_author = NULL;
    bool recurse, remove_lock = false;
    unsigned char *digest = NULL;
    svn_revnum_t new_revnum;
    PyObject *py_wcprop_changes = Py_None, *py_path;
    apr_array_header_t *wcprop_changes = NULL;
    AdmObject *admobj = (AdmObject *)self;
    apr_pool_t *temp_pool;
    int digest_len;
    bool remove_changelist = false;
    char *kwnames[] = { "path", "recurse", "new_revnum", "rev_date", "rev_author",
        "wcprop_changes", "remove_lock", "digest", "remove_changelist", NULL };

    if (!PyArg_ParseTupleAndKeywords(args, kwargs, "Oblzz|Obz#b", kwnames,
                                     &py_path, &recurse, &new_revnum, &rev_date,
                                     &rev_author, &py_wcprop_changes,
                                     &remove_lock, &digest, &digest_len, &remove_changelist))
        return NULL;

    PyErr_WarnEx(PyExc_DeprecationWarning, "process_committed is deprecated. Use process_committed_queue instead.", 2);

    ADM_CHECK_CLOSED(admobj);

    temp_pool = Pool(NULL);
    if (temp_pool == NULL) {
        return NULL;
    }

    path = py_object_to_svn_abspath(py_path, temp_pool);
    if (path == NULL) {
        apr_pool_destroy(temp_pool);
        return NULL;
    }

    if (!py_dict_to_wcprop_changes(py_wcprop_changes, temp_pool, &wcprop_changes)) {
        apr_pool_destroy(temp_pool);
        return NULL;
    }

#if ONLY_SINCE_SVN(1, 6)
    RUN_SVN_WITH_POOL(temp_pool, svn_wc_process_committed4(
                                                           path, admobj->adm, recurse, new_revnum,
                                                           rev_date, rev_author, wcprop_changes,
                                                           remove_lock, remove_changelist?TRUE:FALSE, digest, temp_pool));
#else
    if (remove_changelist) {
        PyErr_SetString(PyExc_NotImplementedError, "remove_changelist only supported in svn < 1.6");
        apr_pool_destroy(temp_pool);
        return NULL;
    }
    RUN_SVN_WITH_POOL(temp_pool, svn_wc_process_committed3(path, admobj->adm, recurse, new_revnum,
                                                           rev_date, rev_author, wcprop_changes,
                                                           remove_lock, digest, temp_pool));
#endif

    apr_pool_destroy(temp_pool);

    Py_RETURN_NONE;
}

static PyObject *adm_close(PyObject *self)
{
    AdmObject *admobj = (AdmObject *)self;
    if (admobj->adm != NULL) {
#if ONLY_SINCE_SVN(1, 6)
        apr_pool_t *temp_pool = Pool(NULL);
        Py_BEGIN_ALLOW_THREADS
            svn_wc_adm_close2(admobj->adm, temp_pool);
        apr_pool_destroy(temp_pool);
#else
        Py_BEGIN_ALLOW_THREADS
            svn_wc_adm_close(admobj->adm);
#endif
        Py_END_ALLOW_THREADS
            admobj->adm = NULL;
    }

    Py_RETURN_NONE;
}

static void adm_dealloc(PyObject *self)
{
    apr_pool_destroy(((AdmObject *)self)->pool);
    PyObject_Del(self);
}

static PyObject *adm_repr(PyObject *self)
{
    AdmObject *admobj = (AdmObject *)self;

    if (admobj->adm == NULL) {
        return PyRepr_FromFormat("<wc.WorkingCopy (closed) at 0x%p>", admobj);
    } else {
        return PyRepr_FromFormat("<wc.WorkingCopy at '%s'>",
                                 svn_wc_adm_access_path(admobj->adm));
    }
}

static PyObject *adm_remove_lock(PyObject *self, PyObject *args)
{
    const char *path;
    PyObject *py_path;
    AdmObject *admobj = (AdmObject *)self;
    apr_pool_t *temp_pool;

    if (!PyArg_ParseTuple(args, "O", &py_path))
        return NULL;

    ADM_CHECK_CLOSED(admobj);

    temp_pool = Pool(NULL);
    if (temp_pool == NULL) {
        return NULL;
    }

    path = py_object_to_svn_abspath(py_path, temp_pool);
    if (path == NULL) {
        apr_pool_destroy(temp_pool);
        return NULL;
    }

    RUN_SVN_WITH_POOL(temp_pool, svn_wc_remove_lock(path, admobj->adm, temp_pool))

        apr_pool_destroy(temp_pool);

    Py_RETURN_NONE;
}

static PyObject *get_ancestry(PyObject *self, PyObject *args)
{
    const char *path;
    char *url;
    svn_revnum_t rev;
    apr_pool_t *temp_pool;
    PyObject *py_path;
    AdmObject *admobj = (AdmObject *)self;

    if (!PyArg_ParseTuple(args, "O", &py_path))
        return NULL;

    ADM_CHECK_CLOSED(admobj);

    temp_pool = Pool(NULL);
    if (temp_pool == NULL) {
        return NULL;
    }

    path = py_object_to_svn_abspath(py_path, temp_pool);
    if (path == NULL) {
        apr_pool_destroy(temp_pool);
        return NULL;
    }

    RUN_SVN_WITH_POOL(temp_pool, svn_wc_get_ancestry(&url, &rev, path, admobj->adm, temp_pool));

    apr_pool_destroy(temp_pool);

    return Py_BuildValue("(si)", url, rev);
}

static PyObject *maybe_set_repos_root(PyObject *self, PyObject *args)
{
    char *path, *repos;
    apr_pool_t *temp_pool;
    AdmObject *admobj = (AdmObject *)self;

    if (!PyArg_ParseTuple(args, "ss", &path, &repos))
        return NULL;

    ADM_CHECK_CLOSED(admobj);

    temp_pool = Pool(NULL);
    if (temp_pool == NULL)
        return NULL;

    RUN_SVN_WITH_POOL(temp_pool, svn_wc_maybe_set_repos_root(admobj->adm, path, repos, temp_pool));

    Py_RETURN_NONE;
}

static PyObject *add_repos_file(PyObject *self, PyObject *args, PyObject *kwargs)
{
    char *kwnames[] = { "dst_path", "new_base_contents", "new_contents",
        "new_base_props", "new_props", "copyfrom_url", "copyfrom_rev",
        "notify", NULL };
    AdmObject *admobj = (AdmObject *)self;
    PyObject *py_dst_path;
    char *copyfrom_url = NULL;
    svn_revnum_t copyfrom_rev = -1;
    PyObject *py_new_base_contents, *py_new_contents, *py_new_base_props,
             *py_new_props, *notify = Py_None;
#if ONLY_SINCE_SVN(1, 6)
    apr_pool_t *temp_pool;
    const char *dst_path;
    svn_stream_t *new_contents, *new_base_contents;
    apr_hash_t *new_props, *new_base_props;
#endif

    if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OOOOO|zlO", kwnames,
                                     &py_dst_path, &py_new_base_contents, &py_new_contents, &py_new_base_props,
                                     &py_new_props, &copyfrom_url, &copyfrom_rev, &notify))
        return NULL;

    ADM_CHECK_CLOSED(admobj);

#if ONLY_SINCE_SVN(1, 6)
    temp_pool = Pool(NULL);
    if (temp_pool == NULL)
        return NULL;

    new_base_props = prop_dict_to_hash(temp_pool, py_new_base_props);

    new_props = prop_dict_to_hash(temp_pool, py_new_props);

    new_base_contents = new_py_stream(temp_pool, py_new_base_contents);

    new_contents = new_py_stream(temp_pool, py_new_contents);

    dst_path = py_object_to_svn_abspath(py_dst_path, temp_pool);

    RUN_SVN_WITH_POOL(temp_pool, svn_wc_add_repos_file3(dst_path, admobj->adm,
                                                        new_base_contents,
                                                        new_contents,
                                                        new_base_props,
                                                        new_props,
                                                        copyfrom_url, copyfrom_rev,
                                                        py_cancel_check, NULL,
                                                        py_wc_notify_func, notify, temp_pool));

    apr_pool_destroy(temp_pool);

    Py_RETURN_NONE;

#else
    PyErr_SetString(PyExc_NotImplementedError,
                    "add_repos_file3 not supported on svn < 1.6");
    return NULL;
#endif
}

static PyObject *mark_missing_deleted(PyObject *self, PyObject *args)
{
    const char *path;
    AdmObject *admobj = (AdmObject *)self;
    apr_pool_t *temp_pool;
    PyObject *py_path;

    if (!PyArg_ParseTuple(args, "O", &py_path))
        return NULL;

    ADM_CHECK_CLOSED(admobj);

    temp_pool = Pool(NULL);
    if (temp_pool == NULL) {
        return NULL;
    }

    path = py_object_to_svn_abspath(py_path, temp_pool);
    if (path == NULL) {
        apr_pool_destroy(temp_pool);
        return NULL;
    }

    RUN_SVN_WITH_POOL(temp_pool, svn_wc_mark_missing_deleted(path, admobj->adm, temp_pool));

    apr_pool_destroy(temp_pool);

    Py_RETURN_NONE;
}

static PyObject *remove_from_revision_control(PyObject *self, PyObject *args)
{
    char *name;
    bool destroy_wf = false, instant_error = false;
    AdmObject *admobj = (AdmObject *)self;
    apr_pool_t *temp_pool;

    if (!PyArg_ParseTuple(args, "s|bb", &name, &destroy_wf, &instant_error))
        return NULL;

    ADM_CHECK_CLOSED(admobj);

    temp_pool = Pool(NULL);
    if (temp_pool == NULL)
        return NULL;

    RUN_SVN_WITH_POOL(temp_pool,
                      svn_wc_remove_from_revision_control(admobj->adm, name,
                                                          destroy_wf?TRUE:FALSE, instant_error?TRUE:FALSE, py_cancel_check, NULL, temp_pool));

    apr_pool_destroy(temp_pool);

    Py_RETURN_NONE;
}

static PyObject *relocate(PyObject *self, PyObject *args)
{
    const char *path;
    char *from, *to;
    AdmObject *admobj = (AdmObject *)self;
    apr_pool_t *temp_pool;
    bool recurse = true;
    PyObject *py_validator = Py_None, *py_path;

    if (!PyArg_ParseTuple(args, "Oss|bO:relocate", &py_path, &from, &to, &recurse,
                          &py_validator))
        return NULL;

    ADM_CHECK_CLOSED(admobj);

    temp_pool = Pool(NULL);
    if (temp_pool == NULL) {
        return NULL;
    }

    path = py_object_to_svn_abspath(py_path, temp_pool);
    if (path == NULL) {
        apr_pool_destroy(temp_pool);
        return NULL;
    }

#if ONLY_SINCE_SVN(1, 6)
    RUN_SVN_WITH_POOL(temp_pool, svn_wc_relocate3(path, admobj->adm, from, to, recurse?TRUE:FALSE, wc_validator3, py_validator, temp_pool));
#else
    RUN_SVN_WITH_POOL(temp_pool, svn_wc_relocate2(path, admobj->adm, from, to, recurse?TRUE:FALSE, wc_validator2, py_validator, temp_pool));
#endif

    apr_pool_destroy(temp_pool);

    Py_RETURN_NONE;
}

static PyObject *crop_tree(PyObject *self, PyObject *args)
{
    char *target;
    svn_depth_t depth;
    PyObject *notify;
#if ONLY_SINCE_SVN(1, 6)
    apr_pool_t *temp_pool;
#endif
    AdmObject *admobj = (AdmObject *)self;

    if (!PyArg_ParseTuple(args, "si|O", &target, &depth, &notify))
        return NULL;

    ADM_CHECK_CLOSED(admobj);

#if ONLY_SINCE_SVN(1, 6)
    temp_pool = Pool(NULL);
    if (temp_pool == NULL)
        return NULL;

    RUN_SVN_WITH_POOL(temp_pool, svn_wc_crop_tree(admobj->adm,
                                                  target, depth, py_wc_notify_func, notify,
                                                  py_cancel_check, NULL, temp_pool));

    apr_pool_destroy(temp_pool);

    Py_RETURN_NONE;
#else
    PyErr_SetString(PyExc_NotImplementedError,
                    "crop_tree only available on subversion < 1.6");
    return NULL;
#endif
}

static PyObject *translated_stream(PyObject *self, PyObject *args)
{
    const char *path, *versioned_file;
    StreamObject *ret;
    svn_stream_t *stream;
    AdmObject *admobj = (AdmObject *)self;
    apr_pool_t *stream_pool;
    PyObject *py_path, *py_versioned_file;
    int flags;

    if (!PyArg_ParseTuple(args, "OOi", &py_path, &py_versioned_file, &flags))
        return NULL;

#if ONLY_SINCE_SVN(1, 5)
    ADM_CHECK_CLOSED(admobj);

    stream_pool = Pool(NULL);
    if (stream_pool == NULL)
        return NULL;

    path = py_object_to_svn_abspath(py_path, stream_pool);
    if (path == NULL) {
        apr_pool_destroy(stream_pool);
        return NULL;
    }

    versioned_file = py_object_to_svn_abspath(py_versioned_file, stream_pool);
    if (versioned_file == NULL) {
        apr_pool_destroy(stream_pool);
{'fraction_non_alphanumeric': 0.09639953542392567, 'fraction_numerical': 0.0031580733142804944, 'mean_word_length': 2.1445689195289095, 'pattern_counts': {'":': 0, '<': 53, '<?xml version=': 0, '>': 137, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 336, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6915985', 'n_tokens_mistral': 25414, 'n_tokens_neox': 25055, 'n_words': 4860}