Add py_object_to_svn_relpath.
authorJelmer Vernooij <jelmer@jelmer.uk>
Sat, 9 Jul 2016 21:43:24 +0000 (21:43 +0000)
committerJelmer Vernooij <jelmer@jelmer.uk>
Sat, 9 Jul 2016 21:43:24 +0000 (21:43 +0000)
subvertpy/_ra.c
subvertpy/editor.c
subvertpy/tests/test_ra.py
subvertpy/util.c
subvertpy/util.h

index 0e900b08c5ebd97368bb80e025025c4c5c735ccc..defef94d624af3ff064dc9260e2221be99bec2ac 100644 (file)
@@ -1380,13 +1380,14 @@ static PyObject *ra_get_dir(PyObject *self, PyObject *args, PyObject *kwargs)
        svn_dirent_t *dirent;
        apr_ssize_t klen;
        char *path;
+       PyObject *py_path;
        svn_revnum_t revision = -1;
        unsigned int dirent_fields = 0;
        PyObject *py_dirents, *py_props;
     char *kwnames[] = { "path", "revision", "fields", NULL };
 
-       if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|lI:get_dir", kwnames,
-                                     &path, &revision, &dirent_fields))
+       if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|lI:get_dir", kwnames,
+                                     &py_path, &revision, &dirent_fields))
                return NULL;
 
        if (ra_check_busy(ra))
@@ -1399,11 +1400,15 @@ static PyObject *ra_get_dir(PyObject *self, PyObject *args, PyObject *kwargs)
        if (revision != SVN_INVALID_REVNUM)
                fetch_rev = revision;
 
+       path = py_object_to_svn_relpath(py_path, temp_pool);
+       if (path == NULL)
+               return NULL;
+
        /* Yuck. Subversion doesn't like leading slashes.. */
        while (*path == '/') path++;
 
        RUN_RA_WITH_POOL(temp_pool, ra, svn_ra_get_dir2(ra->ra, &dirents, &fetch_rev, &props,
-                                        svn_relpath_canonicalize(path, temp_pool), revision, dirent_fields, temp_pool));
+                                        path, revision, dirent_fields, temp_pool));
 
        if (dirents == NULL) {
                py_dirents = Py_None;
@@ -1455,6 +1460,7 @@ fail:
 static PyObject *ra_get_file(PyObject *self, PyObject *args)
 {
        char *path;
+       PyObject *py_path;
        svn_revnum_t revision = -1;
        RemoteAccessObject *ra = (RemoteAccessObject *)self;
        apr_hash_t *props;
@@ -1462,7 +1468,7 @@ static PyObject *ra_get_file(PyObject *self, PyObject *args)
        PyObject *py_stream, *py_props;
        apr_pool_t *temp_pool;
 
-       if (!PyArg_ParseTuple(args, "sO|l:get_file", &path, &py_stream, &revision))
+       if (!PyArg_ParseTuple(args, "OO|l:get_file", &py_path, &py_stream, &revision))
                return NULL;
 
        if (ra_check_busy(ra))
@@ -1475,11 +1481,15 @@ static PyObject *ra_get_file(PyObject *self, PyObject *args)
        if (revision != SVN_INVALID_REVNUM)
                fetch_rev = revision;
 
+       path = py_object_to_svn_relpath(py_path, temp_pool);
+       if (path == NULL)
+               return NULL;
+
        /* Yuck. Subversion doesn't like leading slashes.. */
        while (*path == '/') path++;
 
-       RUN_RA_WITH_POOL(temp_pool, ra, svn_ra_get_file(ra->ra, svn_relpath_canonicalize(path, temp_pool), revision, 
-                                                                                                       new_py_stream(temp_pool, py_stream), 
+       RUN_RA_WITH_POOL(temp_pool, ra, svn_ra_get_file(ra->ra, path, revision,
+                                                                                                       new_py_stream(temp_pool, py_stream),
                                                                                                        &fetch_rev, &props, temp_pool));
 
        py_props = prop_hash_to_dict(props);
@@ -1517,15 +1527,14 @@ static PyObject *ra_get_lock(PyObject *self, PyObject *args)
 
 static PyObject *ra_check_path(PyObject *self, PyObject *args)
 {
-       char *path; 
+       char *path;
        RemoteAccessObject *ra = (RemoteAccessObject *)self;
        svn_revnum_t revision;
        svn_node_kind_t kind;
+       PyObject *py_path;
        apr_pool_t *temp_pool;
 
-       if (!PyArg_ParseTuple(args, "sl:check_path", &path, &revision))
-               return NULL;
-       if (ra_check_svn_path(path))
+       if (!PyArg_ParseTuple(args, "Ol:check_path", &py_path, &revision))
                return NULL;
        if (ra_check_busy(ra))
                return NULL;
@@ -1534,8 +1543,15 @@ static PyObject *ra_check_path(PyObject *self, PyObject *args)
        if (temp_pool == NULL)
                return NULL;
 
+       path = py_object_to_svn_relpath(py_path, temp_pool);
+       if (path == NULL)
+               return NULL;
+
+       if (ra_check_svn_path(path))
+               return NULL;
+
        RUN_RA_WITH_POOL(temp_pool, ra,
-                                         svn_ra_check_path(ra->ra, svn_relpath_canonicalize(path, temp_pool), revision, &kind, 
+                                         svn_ra_check_path(ra->ra, path, revision, &kind, 
                                         temp_pool));
        apr_pool_destroy(temp_pool);
        return PyInt_FromLong(kind);
@@ -1543,16 +1559,15 @@ static PyObject *ra_check_path(PyObject *self, PyObject *args)
 
 static PyObject *ra_stat(PyObject *self, PyObject *args)
 {
-       char *path; 
+       char *path;
+       PyObject *py_path;
        RemoteAccessObject *ra = (RemoteAccessObject *)self;
        PyObject *ret;
        svn_revnum_t revision;
        svn_dirent_t *dirent;
        apr_pool_t *temp_pool;
 
-       if (!PyArg_ParseTuple(args, "sl:stat", &path, &revision))
-               return NULL;
-       if (ra_check_svn_path(path))
+       if (!PyArg_ParseTuple(args, "Ol:stat", &py_path, &revision))
                return NULL;
        if (ra_check_busy(ra))
                return NULL;
@@ -1561,8 +1576,15 @@ static PyObject *ra_stat(PyObject *self, PyObject *args)
        if (temp_pool == NULL)
                return NULL;
 
+       path = py_object_to_svn_relpath(py_path, temp_pool);
+       if (path == NULL)
+               return NULL;
+
+       if (ra_check_svn_path(path))
+               return NULL;
+
        RUN_RA_WITH_POOL(temp_pool, ra,
-                                         svn_ra_stat(ra->ra, svn_relpath_canonicalize(path, temp_pool), revision, &dirent,
+                                         svn_ra_stat(ra->ra, path, revision, &dirent,
                                         temp_pool));
        ret = py_dirent(dirent, SVN_DIRENT_ALL);
        apr_pool_destroy(temp_pool);
@@ -1683,6 +1705,7 @@ fail_busy:
 static PyObject *ra_get_locks(PyObject *self, PyObject *args)
 {
        char *path;
+       PyObject *py_path;
        apr_pool_t *temp_pool;
        apr_hash_t *hash_locks;
        apr_hash_index_t *idx;
@@ -1692,10 +1715,7 @@ static PyObject *ra_get_locks(PyObject *self, PyObject *args)
        svn_lock_t *lock;
        PyObject *ret;
 
-       if (!PyArg_ParseTuple(args, "s:get_locks", &path))
-               return NULL;
-
-       if (ra_check_svn_path(path))
+       if (!PyArg_ParseTuple(args, "O:get_locks", &py_path))
                return NULL;
 
        if (ra_check_busy(ra))
@@ -1705,6 +1725,13 @@ static PyObject *ra_get_locks(PyObject *self, PyObject *args)
        if (temp_pool == NULL)
                return NULL;
 
+       path = py_object_to_svn_relpath(py_path, temp_pool);
+       if (path == NULL)
+               return NULL;
+
+       if (ra_check_svn_path(path))
+               return NULL;
+
        RUN_RA_WITH_POOL(temp_pool, ra, svn_ra_get_locks(ra->ra, &hash_locks, path, temp_pool));
 
        ret = PyDict_New();
@@ -1738,6 +1765,7 @@ static PyObject *ra_get_locks(PyObject *self, PyObject *args)
 static PyObject *ra_get_locations(PyObject *self, PyObject *args)
 {
        char *path;
+       PyObject *py_path;
        RemoteAccessObject *ra = (RemoteAccessObject *)self;
        svn_revnum_t peg_revision;
        PyObject *location_revisions;
@@ -1749,10 +1777,7 @@ static PyObject *ra_get_locations(PyObject *self, PyObject *args)
        apr_ssize_t klen;
        char *val;
 
-       if (!PyArg_ParseTuple(args, "slO:get_locations", &path, &peg_revision, &location_revisions))
-               goto fail_busy;
-
-       if (ra_check_svn_path(path))
+       if (!PyArg_ParseTuple(args, "OlO:get_locations", &py_path, &peg_revision, &location_revisions))
                goto fail_busy;
 
        if (ra_check_busy(ra))
@@ -1762,8 +1787,15 @@ static PyObject *ra_get_locations(PyObject *self, PyObject *args)
        if (temp_pool == NULL)
                goto fail_pool;
 
+       path = py_object_to_svn_relpath(py_path, temp_pool);
+       if (path == NULL)
+               goto fail_dict;
+
+       if (ra_check_svn_path(path))
+               goto fail_dict;
+
        RUN_RA_WITH_POOL(temp_pool, ra, svn_ra_get_locations(ra->ra, &hash_locations,
-                                       svn_relpath_canonicalize(path, temp_pool), peg_revision, 
+                                       path, peg_revision,
                                        revnum_list_to_apr_array(temp_pool, location_revisions),
                                        temp_pool));
        ret = PyDict_New();
@@ -1948,20 +1980,25 @@ static PyObject *ra_get_location_segments(PyObject *self, PyObject *args)
        RemoteAccessObject *ra = (RemoteAccessObject *)self;
        svn_revnum_t peg_revision, start_revision, end_revision;
        char *path;
+       PyObject *py_path;
        PyObject *py_rcvr;
        apr_pool_t *temp_pool;
 
-       if (!PyArg_ParseTuple(args, "slllO:get_location_segments", &path,
+       if (!PyArg_ParseTuple(args, "OlllO:get_location_segments", &py_path,
                        &peg_revision, &start_revision, &end_revision, &py_rcvr))
                return NULL;
 
-       if (ra_check_svn_path(path))
-               return NULL;
-
        temp_pool = Pool(NULL);
        if (temp_pool == NULL)
                return NULL;
 
+       path = py_object_to_svn_relpath(py_path, temp_pool);
+       if (path == NULL)
+               return NULL;
+
+       if (ra_check_svn_path(path))
+               return NULL;
+
        RUN_RA_WITH_POOL(temp_pool, ra, svn_ra_get_location_segments(ra->ra, 
                                         path, peg_revision, start_revision, end_revision,
                                         py_location_segment_receiver, 
index 3a196276bb079317cb12aef068274b7150950a3e..08c3b07322a9a93edd328f4883dfed5c3b0a5c06 100644 (file)
@@ -364,10 +364,11 @@ PyTypeObject FileEditor_Type = {
 static PyObject *py_dir_editor_delete_entry(PyObject *self, PyObject *args)
 {
        EditorObject *editor = (EditorObject *)self;
-       char *path; 
+       char *path;
+       PyObject *py_path;
        svn_revnum_t revision = -1;
 
-       if (!PyArg_ParseTuple(args, "s|l", &path, &revision))
+       if (!PyArg_ParseTuple(args, "O|l", &py_path, &revision))
                return NULL;
 
        if (editor->done) {
@@ -380,22 +381,27 @@ static PyObject *py_dir_editor_delete_entry(PyObject *self, PyObject *args)
                return NULL;
        }
 
-       RUN_SVN(editor->editor->delete_entry(svn_relpath_canonicalize(path, editor->pool),
-                                                                                revision, editor->baton, editor->pool));
+       path = py_object_to_svn_relpath(py_path, editor->pool);
+       if (path == NULL) {
+               return NULL;
+       }
+
+       RUN_SVN(editor->editor->delete_entry(path, revision, editor->baton, editor->pool));
 
        Py_RETURN_NONE;
 }
 
 static PyObject *py_dir_editor_add_directory(PyObject *self, PyObject *args)
 {
+       PyObject *py_path;
        char *path;
-       char *copyfrom_path=NULL; 
-       svn_revnum_t copyfrom_rev=-1;
-       void *child_baton;
+       char *copyfrom_path = NULL;
+       svn_revnum_t copyfrom_rev = -1;
+       void *child_baton;
        EditorObject *editor = (EditorObject *)self;
        apr_pool_t *subpool;
 
-       if (!PyArg_ParseTuple(args, "s|zl", &path, &copyfrom_path, &copyfrom_rev))
+       if (!PyArg_ParseTuple(args, "O|zl", &py_path, &copyfrom_path, &copyfrom_rev))
                return NULL;
 
        if (editor->done) {
@@ -408,9 +414,14 @@ static PyObject *py_dir_editor_add_directory(PyObject *self, PyObject *args)
                return NULL;
        }
 
+       path = py_object_to_svn_relpath(py_path, editor->pool);
+       if (path == NULL) {
+               return NULL;
+       }
+
        RUN_SVN(editor->editor->add_directory(
-               svn_relpath_canonicalize(path, editor->pool), editor->baton,
-               copyfrom_path == NULL?NULL:svn_uri_canonicalize(copyfrom_path, editor->pool), 
+               path, editor->baton,
+               copyfrom_path == NULL?NULL:svn_uri_canonicalize(copyfrom_path, editor->pool),
                copyfrom_rev, editor->pool, &child_baton));
 
        subpool = Pool(editor->pool);
@@ -424,12 +435,13 @@ static PyObject *py_dir_editor_add_directory(PyObject *self, PyObject *args)
 static PyObject *py_dir_editor_open_directory(PyObject *self, PyObject *args)
 {
        char *path;
+       PyObject *py_path;
        EditorObject *editor = (EditorObject *)self;
        svn_revnum_t base_revision=-1;
        void *child_baton;
        apr_pool_t *subpool;
 
-       if (!PyArg_ParseTuple(args, "s|l", &path, &base_revision))
+       if (!PyArg_ParseTuple(args, "O|l", &py_path, &base_revision))
                return NULL;
 
        if (editor->done) {
@@ -442,15 +454,19 @@ static PyObject *py_dir_editor_open_directory(PyObject *self, PyObject *args)
                return NULL;
        }
 
+       path = py_object_to_svn_relpath(py_path, editor->pool);
+       if (path == NULL) {
+               return NULL;
+       }
+
        RUN_SVN(editor->editor->open_directory(
-               svn_relpath_canonicalize(path, editor->pool), editor->baton,
-                                       base_revision, editor->pool, &child_baton));
+               path, editor->baton, base_revision, editor->pool, &child_baton));
 
        subpool = Pool(NULL);
        if (subpool == NULL)
                return NULL;
 
-       return new_editor_object(editor, editor->editor, child_baton, subpool, 
+       return new_editor_object(editor, editor->editor, child_baton, subpool,
                                                         &DirectoryEditor_Type, NULL, NULL, NULL);
 }
 
@@ -476,7 +492,7 @@ static PyObject *py_dir_editor_change_prop(PyObject *self, PyObject *args)
 
        c_value.len = vallen;
 
-       RUN_SVN(editor->editor->change_dir_prop(editor->baton, name, 
+       RUN_SVN(editor->editor->change_dir_prop(editor->baton, name,
                                        (c_value.data == NULL)?NULL:&c_value, editor->pool));
 
        Py_RETURN_NONE;
@@ -513,9 +529,10 @@ static PyObject *py_dir_editor_close(PyObject *self)
 static PyObject *py_dir_editor_absent_directory(PyObject *self, PyObject *args)
 {
        char *path;
+       PyObject *py_path;
        EditorObject *editor = (EditorObject *)self;
 
-       if (!PyArg_ParseTuple(args, "s", &path))
+       if (!PyArg_ParseTuple(args, "O", &py_path))
                return NULL;
 
        if (editor->done) {
@@ -528,8 +545,13 @@ static PyObject *py_dir_editor_absent_directory(PyObject *self, PyObject *args)
                return NULL;
        }
 
+       path = py_object_to_svn_relpath(py_path, editor->pool);
+       if (path == NULL) {
+               return NULL;
+       }
+
        RUN_SVN(editor->editor->absent_directory(
-               svn_relpath_canonicalize(path, editor->pool), editor->baton, editor->pool));
+               path, editor->baton, editor->pool));
 
        Py_RETURN_NONE;
 }
@@ -537,12 +559,13 @@ static PyObject *py_dir_editor_absent_directory(PyObject *self, PyObject *args)
 static PyObject *py_dir_editor_add_file(PyObject *self, PyObject *args)
 {
        char *path, *copy_path=NULL;
+       PyObject *py_path;
        svn_revnum_t copy_rev=-1;
        void *file_baton = NULL;
        EditorObject *editor = (EditorObject *)self;
        apr_pool_t *subpool;
 
-       if (!PyArg_ParseTuple(args, "s|zl", &path, &copy_path, &copy_rev))
+       if (!PyArg_ParseTuple(args, "O|zl", &py_path, &copy_path, &copy_rev))
                return NULL;
 
        if (editor->done) {
@@ -555,8 +578,12 @@ static PyObject *py_dir_editor_add_file(PyObject *self, PyObject *args)
                return NULL;
        }
 
-       RUN_SVN(editor->editor->add_file(svn_relpath_canonicalize(path, editor->pool),
-               editor->baton, 
+       path = py_object_to_svn_relpath(py_path, editor->pool);
+       if (path == NULL) {
+               return NULL;
+       }
+
+       RUN_SVN(editor->editor->add_file(path, editor->baton,
                copy_path == NULL?NULL:svn_uri_canonicalize(copy_path, editor->pool),
                copy_rev, editor->pool, &file_baton));
 
@@ -571,12 +598,13 @@ static PyObject *py_dir_editor_add_file(PyObject *self, PyObject *args)
 static PyObject *py_dir_editor_open_file(PyObject *self, PyObject *args)
 {
        char *path;
+       PyObject *py_path;
        svn_revnum_t base_revision=-1;
        void *file_baton;
        EditorObject *editor = (EditorObject *)self;
        apr_pool_t *subpool;
 
-       if (!PyArg_ParseTuple(args, "s|l", &path, &base_revision))
+       if (!PyArg_ParseTuple(args, "O|l", &py_path, &base_revision))
                return NULL;
 
        if (editor->done) {
@@ -589,8 +617,12 @@ static PyObject *py_dir_editor_open_file(PyObject *self, PyObject *args)
                return NULL;
        }
 
-       RUN_SVN(editor->editor->open_file(svn_relpath_canonicalize(path, editor->pool),
-                                                                         editor->baton, base_revision, 
+       path = py_object_to_svn_relpath(py_path, editor->pool);
+       if (path == NULL) {
+               return NULL;
+       }
+
+       RUN_SVN(editor->editor->open_file(path, editor->baton, base_revision,
                                                                          editor->pool, &file_baton));
 
        subpool = Pool(NULL);
@@ -604,9 +636,10 @@ static PyObject *py_dir_editor_open_file(PyObject *self, PyObject *args)
 static PyObject *py_dir_editor_absent_file(PyObject *self, PyObject *args)
 {
        char *path;
+       PyObject *py_path;
        EditorObject *editor = (EditorObject *)self;
 
-       if (!PyArg_ParseTuple(args, "s", &path))
+       if (!PyArg_ParseTuple(args, "O", &py_path))
                return NULL;
 
        if (editor->done) {
@@ -619,8 +652,13 @@ static PyObject *py_dir_editor_absent_file(PyObject *self, PyObject *args)
                return NULL;
        }
 
+       path = py_object_to_svn_relpath(py_path, editor->pool);
+       if (path == NULL) {
+               return NULL;
+       }
+
        RUN_SVN(editor->editor->absent_file(
-               svn_relpath_canonicalize(path, editor->pool), editor->baton, editor->pool));
+               path, editor->baton, editor->pool));
 
        Py_RETURN_NONE;
 }
@@ -676,65 +714,64 @@ static PyMethodDef py_dir_editor_methods[] = {
        { NULL, }
 };
 
-PyTypeObject DirectoryEditor_Type = { 
+PyTypeObject DirectoryEditor_Type = {
        PyObject_HEAD_INIT(NULL) 0,
        "_ra.DirEditor", /*     const char *tp_name;  For printing, in format "<module>.<name>" */
-       sizeof(EditorObject), 
+       sizeof(EditorObject),
        0,/*    Py_ssize_t tp_basicsize, tp_itemsize;  For allocation */
-       
+
        /* Methods to implement standard operations */
-       
+
        py_editor_dealloc, /* destructor tp_dealloc;  */
        NULL, /*        printfunc tp_print;     */
        NULL, /*        getattrfunc tp_getattr; */
        NULL, /*        setattrfunc tp_setattr; */
        NULL, /*        cmpfunc tp_compare;     */
        NULL, /*        reprfunc tp_repr;       */
-       
+
        /* Method suites for standard classes */
-       
+
        NULL, /*        PyNumberMethods *tp_as_number;  */
        NULL, /*        PySequenceMethods *tp_as_sequence;      */
        NULL, /*        PyMappingMethods *tp_as_mapping;        */
-       
+
        /* More standard operations (here for binary compatibility) */
-       
+
        NULL, /*        hashfunc tp_hash;       */
        NULL, /*        ternaryfunc tp_call;    */
        NULL, /*        reprfunc tp_str;        */
        NULL, /*        getattrofunc tp_getattro;       */
        NULL, /*        setattrofunc tp_setattro;       */
-       
+
        /* Functions to access object as input/output buffer */
        NULL, /*        PyBufferProcs *tp_as_buffer;    */
-       
+
        /* Flags to define presence of optional/expanded features */
        0, /*   long tp_flags;  */
-       
+
        NULL, /*        const char *tp_doc;  Documentation string */
-       
+
        /* Assigned meaning in release 2.0 */
        /* call function for all accessible objects */
        NULL, /*        traverseproc tp_traverse;       */
-       
+
        /* delete references to contained objects */
        NULL, /*        inquiry tp_clear;       */
-       
+
        /* Assigned meaning in release 2.1 */
        /* rich comparisons */
        NULL, /*        richcmpfunc tp_richcompare;     */
-       
+
        /* weak reference enabler */
        0, /*   Py_ssize_t tp_weaklistoffset;   */
-       
+
        /* Added in release 2.2 */
        /* Iterators */
        NULL, /*        getiterfunc tp_iter;    */
        NULL, /*        iternextfunc tp_iternext;       */
-       
+
        /* Attribute descriptor and subclassing stuff */
        py_dir_editor_methods, /*       struct PyMethodDef *tp_methods; */
-       
 };
 
 static PyObject *py_editor_set_target_revision(PyObject *self, PyObject *args)
index 05085d57f947621c0eaac106206879af5a5b793b..f4498acec80ef52dc104a9921dd7e751624e0607 100644 (file)
@@ -348,13 +348,6 @@ class TestRemoteAccess(SubversionTestCase):
         self.assertEqual(NODE_DIR, self.ra.check_path("bar/", 1))
         self.assertEqual(NODE_NONE, self.ra.check_path("blaaaa", 1))
 
-    def test_check_path_with_unsafe_path(self):
-        # The SVN code asserts that paths do not have a leading '/'... And if
-        # that assertion fires, it calls exit(1). That sucks. Make sure it
-        # doesn't happen.
-        self.assertRaises(ValueError, self.ra.check_path, "/bar", 1)
-        self.assertRaises(ValueError, self.ra.check_path, "///bar", 1)
-
     def test_stat(self):
         cb = self.commit_editor()
         cb.add_dir("bar")
@@ -388,10 +381,6 @@ class TestRemoteAccess(SubversionTestCase):
         self.assertEqual({1: "/bar", 2: "/bla", 3: "/bla"}, 
                           self.ra.get_locations("bla", 3, [1,2,3]))
 
-    def test_get_locations_dir_with_unsafe_path(self):
-        # Make sure that an invalid path won't trip an assertion error
-        self.assertRaises(ValueError, self.ra.get_locations, "//bla", 2, [1,2])
-
 
 class AuthTests(TestCase):
 
index 71fe420ee024f2083e87aa5b06f7e6a248cbafeb..373d873e00d68b75230196e989eb86d1de75182a 100644 (file)
@@ -83,6 +83,21 @@ char *py_object_to_svn_uri(PyObject *obj, apr_pool_t *pool)
        }
 }
 
+char *py_object_to_svn_relpath(PyObject *obj, apr_pool_t *pool)
+{
+       if (PyUnicode_Check(obj)) {
+               obj = PyUnicode_AsUTF8String(obj);
+       }
+
+       if (PyBytes_Check(obj)) {
+               return svn_relpath_canonicalize(PyBytes_AsString(obj), pool);
+       } else {
+               PyErr_SetString(PyExc_TypeError,
+                                               "relative paths need to be UTF-8 bytestrings or unicode strings");
+               return NULL;
+       }
+}
+
 apr_pool_t *Pool(apr_pool_t *parent)
 {
        apr_status_t status;
index 672a5a49519441f9a1facd27aa96944a4169c1df..fe13a560bdf825425c688ffc3ca02e335120f79e 100644 (file)
@@ -153,5 +153,6 @@ svn_relpath_canonicalize(const char *relpath,
 #endif
 
 char *py_object_to_svn_uri(PyObject *obj, apr_pool_t *pool);
+char *py_object_to_svn_relpath(PyObject *obj, apr_pool_t *pool);
 
 #endif /* _SUBVERTPY_UTIL_H_ */