Use py_object_to_svn_string.
[jelmer/subvertpy.git] / subvertpy / wc.c
1 /*
2  * Copyright © 2008 Jelmer Vernooij <jelmer@jelmer.uk>
3  * -*- coding: utf-8 -*-
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU Lesser General Public License as published by
7  * the Free Software Foundation; either version 2.1 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
18  */
19 #include <Python.h>
20 #include <apr_general.h>
21 #include <svn_wc.h>
22 #include <svn_path.h>
23 #include <svn_props.h>
24 #include <structmember.h>
25 #include <stdbool.h>
26 #include <apr_md5.h>
27
28 #include "util.h"
29 #include "editor.h"
30 #include "wc.h"
31
32 #ifndef T_BOOL
33 #define T_BOOL T_BYTE
34 #endif
35
36 #if ONLY_SINCE_SVN(1, 5)
37 #define REPORTER_T svn_ra_reporter3_t
38 #else
39 #define REPORTER_T svn_ra_reporter2_t
40 #endif
41
42 static PyTypeObject Entry_Type;
43 static PyTypeObject Status_Type;
44 static PyTypeObject Adm_Type;
45
46 static PyObject *py_entry(const svn_wc_entry_t *entry);
47 static PyObject *py_status(const svn_wc_status2_t *status);
48
49 #if ONLY_BEFORE_SVN(1, 5)
50 struct svn_wc_committed_queue_t
51 {
52         apr_pool_t *pool;
53         apr_array_header_t *queue;
54         svn_boolean_t have_recursive;
55 };
56
57 #if SVN_VER_MINOR < 5
58 typedef struct svn_wc_committed_queue_t svn_wc_committed_queue_t;
59 #endif
60
61 typedef struct
62 {
63         const char *path;
64         svn_wc_adm_access_t *adm_access;
65         svn_boolean_t recurse;
66         svn_boolean_t remove_lock;
67         apr_array_header_t *wcprop_changes;
68         unsigned char *digest;
69 } committed_queue_item_t;
70
71 #if SVN_VER_MINOR < 5
72 static
73 #endif
74 svn_wc_committed_queue_t *svn_wc_committed_queue_create(apr_pool_t *pool)
75 {
76         svn_wc_committed_queue_t *q;
77
78         q = apr_palloc(pool, sizeof(*q));
79         q->pool = pool;
80         q->queue = apr_array_make(pool, 1, sizeof(committed_queue_item_t *));
81         q->have_recursive = FALSE;
82
83         return q;
84 }
85
86 #if SVN_VER_MINOR < 5
87 static
88 #endif
89 svn_error_t *svn_wc_queue_committed(svn_wc_committed_queue_t **queue,
90                         const char *path,
91                         svn_wc_adm_access_t *adm_access,
92                         svn_boolean_t recurse,
93                         apr_array_header_t *wcprop_changes,
94                         svn_boolean_t remove_lock,
95                         svn_boolean_t remove_changelist,
96                         const unsigned char *digest,
97                         apr_pool_t *scratch_pool)
98 {
99   committed_queue_item_t *cqi;
100
101   (*queue)->have_recursive |= recurse;
102
103   /* Use the same pool as the one QUEUE was allocated in,
104      to prevent lifetime issues.  Intermediate operations
105      should use SCRATCH_POOL. */
106
107   /* Add to the array with paths and options */
108   cqi = apr_palloc((*queue)->pool, sizeof(*cqi));
109   cqi->path = path;
110   cqi->adm_access = adm_access;
111   cqi->recurse = recurse;
112   cqi->remove_lock = remove_lock;
113   cqi->wcprop_changes = wcprop_changes;
114   cqi->digest = digest;
115
116   APR_ARRAY_PUSH((*queue)->queue, committed_queue_item_t *) = cqi;
117
118   return SVN_NO_ERROR;
119 }
120
121 #endif
122
123 typedef struct {
124         PyObject_HEAD
125         apr_pool_t *pool;
126         svn_wc_committed_queue_t *queue;
127 } CommittedQueueObject;
128 static PyTypeObject CommittedQueue_Type;
129
130 #if ONLY_SINCE_SVN(1, 5)
131 static svn_error_t *py_ra_report_set_path(void *baton, const char *path, svn_revnum_t revision, svn_depth_t depth, int start_empty, const char *lock_token, apr_pool_t *pool)
132 {
133         PyObject *self = (PyObject *)baton, *py_lock_token, *ret;
134         PyGILState_STATE state = PyGILState_Ensure();
135         if (lock_token == NULL) {
136                 py_lock_token = Py_None;
137                 Py_INCREF(py_lock_token);
138         } else {
139                 py_lock_token = PyString_FromString(lock_token);
140         }
141         ret = PyObject_CallMethod(self, "set_path", "slbOi", path, revision, start_empty, py_lock_token, depth);
142         Py_DECREF(py_lock_token);
143         CB_CHECK_PYRETVAL(ret);
144         Py_DECREF(ret);
145         PyGILState_Release(state);
146         return NULL;
147 }
148
149 static svn_error_t *py_ra_report_link_path(void *report_baton, const char *path, const char *url, svn_revnum_t revision, svn_depth_t depth, int start_empty, const char *lock_token, apr_pool_t *pool)
150 {
151         PyObject *self = (PyObject *)report_baton, *ret, *py_lock_token;
152         PyGILState_STATE state = PyGILState_Ensure();
153         if (lock_token == NULL) {
154                 py_lock_token = Py_None;
155                 Py_INCREF(py_lock_token);
156         } else { 
157                 py_lock_token = PyString_FromString(lock_token);
158         }
159         ret = PyObject_CallMethod(self, "link_path", "sslbOi", path, url, revision, start_empty, py_lock_token, depth);
160         Py_DECREF(py_lock_token);
161         CB_CHECK_PYRETVAL(ret);
162         Py_DECREF(ret);
163         PyGILState_Release(state);
164         return NULL;
165 }
166
167
168 #else
169 static svn_error_t *py_ra_report_set_path(void *baton, const char *path, svn_revnum_t revision, int start_empty, const char *lock_token, apr_pool_t *pool)
170 {
171         PyObject *self = (PyObject *)baton, *py_lock_token, *ret;
172         PyGILState_STATE state = PyGILState_Ensure();
173         if (lock_token == NULL) {
174                 py_lock_token = Py_None;
175                 Py_INCREF(py_lock_token);
176         } else {
177                 py_lock_token = PyString_FromString(lock_token);
178         }
179         ret = PyObject_CallMethod(self, "set_path", "slbOi", path, revision, start_empty, py_lock_token, svn_depth_infinity);
180         CB_CHECK_PYRETVAL(ret);
181         Py_DECREF(ret);
182         PyGILState_Release(state);
183         return NULL;
184 }
185
186 static svn_error_t *py_ra_report_link_path(void *report_baton, const char *path, const char *url, svn_revnum_t revision, int start_empty, const char *lock_token, apr_pool_t *pool)
187 {
188         PyObject *self = (PyObject *)report_baton, *ret, *py_lock_token;
189         PyGILState_STATE state = PyGILState_Ensure();
190         if (lock_token == NULL) {
191                 py_lock_token = Py_None;
192                 Py_INCREF(py_lock_token);
193         } else { 
194                 py_lock_token = PyString_FromString(lock_token);
195         }
196         ret = PyObject_CallMethod(self, "link_path", "sslbOi", path, url, revision, start_empty, py_lock_token, svn_depth_infinity);
197         CB_CHECK_PYRETVAL(ret);
198         Py_DECREF(ret);
199         PyGILState_Release(state);
200         return NULL;
201 }
202
203
204 #endif
205
206 static svn_error_t *py_ra_report_delete_path(void *baton, const char *path, apr_pool_t *pool)
207 {
208         PyObject *self = (PyObject *)baton, *ret;
209         PyGILState_STATE state = PyGILState_Ensure();
210         ret = PyObject_CallMethod(self, "delete_path", "s", path);
211         CB_CHECK_PYRETVAL(ret);
212         Py_DECREF(ret);
213         PyGILState_Release(state);
214         return NULL;
215 }
216
217 static svn_error_t *py_ra_report_finish(void *baton, apr_pool_t *pool)
218 {
219         PyObject *self = (PyObject *)baton, *ret;
220         PyGILState_STATE state = PyGILState_Ensure();
221         ret = PyObject_CallMethod(self, "finish", "");
222         CB_CHECK_PYRETVAL(ret);
223         Py_DECREF(ret);
224         PyGILState_Release(state);
225         return NULL;
226 }
227
228 static svn_error_t *py_ra_report_abort(void *baton, apr_pool_t *pool)
229 {
230         PyObject *self = (PyObject *)baton, *ret;
231         PyGILState_STATE state = PyGILState_Ensure();
232         ret = PyObject_CallMethod(self, "abort", "");
233         CB_CHECK_PYRETVAL(ret);
234         Py_DECREF(ret);
235         PyGILState_Release(state);
236         return NULL;
237 }
238
239 static const REPORTER_T py_ra_reporter = {
240         py_ra_report_set_path,
241         py_ra_report_delete_path,
242         py_ra_report_link_path,
243         py_ra_report_finish,
244         py_ra_report_abort,
245 };
246
247
248
249 /**
250  * Get runtime libsvn_wc version information.
251  *
252  * :return: tuple with major, minor, patch version number and tag.
253  */
254 static PyObject *version(PyObject *self)
255 {
256         const svn_version_t *ver = svn_wc_version();
257         return Py_BuildValue("(iiis)", ver->major, ver->minor, 
258                                                  ver->patch, ver->tag);
259 }
260
261 SVN_VERSION_DEFINE(svn_api_version);
262
263 /**
264  * Get compile-time libsvn_wc version information.
265  *
266  * :return: tuple with major, minor, patch version number and tag.
267  */
268 static PyObject *api_version(PyObject *self)
269 {
270         const svn_version_t *ver = &svn_api_version;
271         return Py_BuildValue("(iiis)", ver->major, ver->minor, 
272                                                  ver->patch, ver->tag);
273 }
274
275 static svn_error_t *py_wc_found_entry(const char *path, const svn_wc_entry_t *entry, void *walk_baton, apr_pool_t *pool)
276 {
277         PyObject *fn, *ret;
278         PyObject *callbacks = (PyObject *)walk_baton;
279         PyGILState_STATE state = PyGILState_Ensure();
280         if (PyTuple_Check(callbacks)) {
281                 fn = PyTuple_GET_ITEM(callbacks, 0);
282         } else {
283                 fn = (PyObject *)walk_baton;
284         }
285         ret = PyObject_CallFunction(fn, "sO", path, py_entry(entry));
286         CB_CHECK_PYRETVAL(ret);
287         Py_DECREF(ret);
288         PyGILState_Release(state);
289         return NULL;
290 }
291
292 #if ONLY_SINCE_SVN(1, 5)
293
294 svn_error_t *py_wc_handle_error(const char *path, svn_error_t *err, void *walk_baton, apr_pool_t *pool)
295 {
296         PyObject *fn, *ret;
297         PyObject *py_err;
298         PyGILState_STATE state;
299         PyObject *callbacks = (PyObject *)walk_baton;
300         if (PyTuple_Check(callbacks)) {
301                 fn = PyTuple_GET_ITEM(callbacks, 1);
302         } else {
303                 return err;
304         }
305         state = PyGILState_Ensure();
306         py_err = PyErr_NewSubversionException(err);
307         ret = PyObject_CallFunction(fn, "sO", path, py_err);
308         CB_CHECK_PYRETVAL(ret);
309         Py_DECREF(ret);
310         PyGILState_Release(state);
311         Py_DECREF(py_err);
312         return NULL;
313 }
314
315 static svn_wc_entry_callbacks2_t py_wc_entry_callbacks2 = {
316         py_wc_found_entry,
317         py_wc_handle_error,
318 };
319 #else
320 static svn_wc_entry_callbacks_t py_wc_entry_callbacks = {
321         py_wc_found_entry
322 };
323
324 #endif
325
326
327 void py_wc_notify_func(void *baton, const svn_wc_notify_t *notify, apr_pool_t *pool)
328 {
329         PyObject *func = baton, *ret;
330         if (func == Py_None)
331                 return;
332
333         if (notify->err != NULL) {
334                 PyObject *excval = PyErr_NewSubversionException(notify->err);
335                 ret = PyObject_CallFunction(func, "O", excval);
336                 Py_DECREF(excval);
337                 Py_XDECREF(ret);
338                 /* If ret was NULL, the cancel func should abort the operation. */
339         }
340 }
341
342 typedef struct {
343         PyObject_HEAD
344         apr_pool_t *pool;
345         svn_wc_entry_t entry;
346 } EntryObject;
347
348 static void entry_dealloc(PyObject *self)
349 {
350         apr_pool_destroy(((EntryObject *)self)->pool);
351         PyObject_Del(self);
352 }
353
354 static PyMemberDef entry_members[] = {
355         { "name", T_STRING, offsetof(EntryObject, entry.name), READONLY, 
356                 "Name of the file"},
357         { "copyfrom_url", T_STRING, offsetof(EntryObject, entry.copyfrom_url), READONLY, 
358                 "Copyfrom location" },
359         { "copyfrom_rev", T_LONG, offsetof(EntryObject, entry.copyfrom_rev), READONLY, 
360                 "Copyfrom revision" },
361         { "uuid", T_STRING, offsetof(EntryObject, entry.uuid), READONLY,
362                 "UUID of repository" },
363         { "url", T_STRING, offsetof(EntryObject, entry.url), READONLY, 
364                 "URL in repository" },
365         { "repos", T_STRING, offsetof(EntryObject, entry.repos), READONLY, 
366                 "Canonical repository URL" },
367         { "schedule", T_INT, offsetof(EntryObject, entry.schedule), READONLY, 
368                 "Scheduling (add, replace, delete, etc)" },
369         { "kind", T_INT, offsetof(EntryObject, entry.kind), READONLY, 
370                 "Kind of file (file, dir, etc)" },
371         { "revision", T_LONG, offsetof(EntryObject, entry.revision), READONLY, 
372                 "Base revision", },
373         { "cmt_rev", T_LONG, offsetof(EntryObject, entry.cmt_rev), READONLY, 
374                 "Last revision this was changed" },
375         { "checksum", T_STRING, offsetof(EntryObject, entry.checksum), READONLY, 
376                 "Hex MD5 checksum for the untranslated text base file" },
377         { "cmt_date", T_LONG, offsetof(EntryObject, entry.cmt_date), READONLY, 
378                 "Last date this was changed" },
379         { "cmt_author", T_STRING, offsetof(EntryObject, entry.cmt_author), READONLY, 
380                 "Last commit author of this item" },
381         { NULL, }
382 };
383
384 static PyTypeObject Entry_Type = {
385         PyObject_HEAD_INIT(NULL) 0,
386         "wc.Entry", /*  const char *tp_name;  For printing, in format "<module>.<name>" */
387         sizeof(EntryObject), 
388         0,/*    Py_ssize_t tp_basicsize, tp_itemsize;  For allocation */
389         
390         /* Methods to implement standard operations */
391         
392         entry_dealloc, /*       destructor tp_dealloc;  */
393         NULL, /*        printfunc tp_print;     */
394         NULL, /*        getattrfunc tp_getattr; */
395         NULL, /*        setattrfunc tp_setattr; */
396         NULL, /*        cmpfunc tp_compare;     */
397         NULL, /*        reprfunc tp_repr;       */
398         
399         /* Method suites for standard classes */
400         
401         NULL, /*        PyNumberMethods *tp_as_number;  */
402         NULL, /*        PySequenceMethods *tp_as_sequence;      */
403         NULL, /*        PyMappingMethods *tp_as_mapping;        */
404         
405         /* More standard operations (here for binary compatibility) */
406         
407         NULL, /*        hashfunc tp_hash;       */
408         NULL, /*        ternaryfunc tp_call;    */
409         NULL, /*        reprfunc tp_str;        */
410         NULL, /*        getattrofunc tp_getattro;       */
411         NULL, /*        setattrofunc tp_setattro;       */
412         
413         /* Functions to access object as input/output buffer */
414         NULL, /*        PyBufferProcs *tp_as_buffer;    */
415         
416         /* Flags to define presence of optional/expanded features */
417         0, /*   long tp_flags;  */
418         
419         NULL, /*        const char *tp_doc;  Documentation string */
420         
421         /* Assigned meaning in release 2.0 */
422         /* call function for all accessible objects */
423         NULL, /*        traverseproc tp_traverse;       */
424         
425         /* delete references to contained objects */
426         NULL, /*        inquiry tp_clear;       */
427         
428         /* Assigned meaning in release 2.1 */
429         /* rich comparisons */
430         NULL, /*        richcmpfunc tp_richcompare;     */
431         
432         /* weak reference enabler */
433         0, /*   Py_ssize_t tp_weaklistoffset;   */
434         
435         /* Added in release 2.2 */
436         /* Iterators */
437         NULL, /*        getiterfunc tp_iter;    */
438         NULL, /*        iternextfunc tp_iternext;       */
439         
440         /* Attribute descriptor and subclassing stuff */
441         NULL, /*        struct PyMethodDef *tp_methods; */
442         entry_members, /*       struct PyMemberDef *tp_members; */
443
444 };
445
446 static PyObject *py_entry(const svn_wc_entry_t *entry)
447 {
448         EntryObject *ret;
449
450         ret = PyObject_New(EntryObject, &Entry_Type);
451         if (ret == NULL)
452                 return NULL;
453
454         ret->pool = Pool(NULL);
455         if (ret->pool == NULL)
456                 return NULL;
457         ret->entry = *svn_wc_entry_dup(entry, ret->pool);
458         return (PyObject *)ret;
459 }
460
461 typedef struct {
462         PyObject_HEAD
463         apr_pool_t *pool;
464         svn_wc_status2_t status;
465         PyObject *entry;
466 } StatusObject;
467
468 static void status_dealloc(PyObject *self)
469 {
470         apr_pool_destroy(((StatusObject *)self)->pool);
471         Py_XDECREF(((StatusObject *)self)->entry);
472         PyObject_Del(self);
473 }
474
475 static PyMemberDef status_members[] = {
476         { "entry", T_OBJECT, offsetof(StatusObject, entry), READONLY, 
477                 "Can be NULL if not under version control." },
478         { "locked", T_BOOL, offsetof(StatusObject, status.locked), READONLY, 
479                 "a directory can be 'locked' if a working copy update was interrupted." },
480         { "copied", T_BOOL, offsetof(StatusObject, status.copied), READONLY, 
481                 "a file or directory can be 'copied' if it's scheduled for addition-with-history (or part of a subtree that is scheduled as such.)." },
482         { "switched", T_BOOL, offsetof(StatusObject, status.switched), READONLY, 
483                 "a file or directory can be 'switched' if the switch command has been used." },
484         { "url", T_STRING, offsetof(StatusObject, status.url), READONLY, 
485                 "URL (actual or expected) in repository" },
486         { "revision", T_LONG, offsetof(StatusObject, status.ood_last_cmt_rev), READONLY, 
487                 "Set to the youngest committed revision, or SVN_INVALID_REVNUM if not out of date.", },
488         { "kind", T_INT, offsetof(StatusObject, status.ood_kind), READONLY, 
489                 "Set to the node kind of the youngest commit, or svn_node_none if not out of date.", },
490         { "status", T_INT, offsetof(StatusObject, status.text_status), READONLY, 
491                 "The status of the entry.", },
492         { NULL, }
493 };
494
495 static PyTypeObject Status_Type = {
496         PyObject_HEAD_INIT(NULL) 0,
497         "wc.Status", /* const char *tp_name;  For printing, in format "<module>.<name>" */
498         sizeof(StatusObject), 
499         0,/*    Py_ssize_t tp_basicsize, tp_itemsize;  For allocation */
500         
501         /* Methods to implement standard operations */
502         
503         status_dealloc, /*      destructor tp_dealloc;  */
504         NULL, /*        printfunc tp_print;     */
505         NULL, /*        getattrfunc tp_getattr; */
506         NULL, /*        setattrfunc tp_setattr; */
507         NULL, /*        cmpfunc tp_compare;     */
508         NULL, /*        reprfunc tp_repr;       */
509         
510         /* Method suites for standard classes */
511         
512         NULL, /*        PyNumberMethods *tp_as_number;  */
513         NULL, /*        PySequenceMethods *tp_as_sequence;      */
514         NULL, /*        PyMappingMethods *tp_as_mapping;        */
515         
516         /* More standard operations (here for binary compatibility) */
517         
518         NULL, /*        hashfunc tp_hash;       */
519         NULL, /*        ternaryfunc tp_call;    */
520         NULL, /*        reprfunc tp_str;        */
521         NULL, /*        getattrofunc tp_getattro;       */
522         NULL, /*        setattrofunc tp_setattro;       */
523         
524         /* Functions to access object as input/output buffer */
525         NULL, /*        PyBufferProcs *tp_as_buffer;    */
526         
527         /* Flags to define presence of optional/expanded features */
528         0, /*   long tp_flags;  */
529         
530         "Working copy status object", /*        const char *tp_doc;  Documentation string */
531         
532         /* Assigned meaning in release 2.0 */
533         /* call function for all accessible objects */
534         NULL, /*        traverseproc tp_traverse;       */
535         
536         /* delete references to contained objects */
537         NULL, /*        inquiry tp_clear;       */
538         
539         /* Assigned meaning in release 2.1 */
540         /* rich comparisons */
541         NULL, /*        richcmpfunc tp_richcompare;     */
542         
543         /* weak reference enabler */
544         0, /*   Py_ssize_t tp_weaklistoffset;   */
545         
546         /* Added in release 2.2 */
547         /* Iterators */
548         NULL, /*        getiterfunc tp_iter;    */
549         NULL, /*        iternextfunc tp_iternext;       */
550         
551         /* Attribute descriptor and subclassing stuff */
552         NULL, /*        struct PyMethodDef *tp_methods; */
553         status_members, /*      struct PyMemberDef *tp_members; */
554
555 };
556
557 static PyObject *py_status(const svn_wc_status2_t *status)
558 {
559         StatusObject *ret;
560         svn_wc_status2_t *dup_status;
561
562         ret = PyObject_New(StatusObject, &Status_Type);
563         if (ret == NULL)
564                 return NULL;
565
566         ret->pool = Pool(NULL);
567         if (ret->pool == NULL) {
568                 PyObject_Del(ret);
569                 return NULL;
570         }
571
572         dup_status = svn_wc_dup_status2(status, ret->pool);
573         if (dup_status == NULL)
574         {
575                 PyErr_NoMemory();
576                 return NULL;
577         }
578         ret->status = *dup_status;
579
580         ret->entry = py_entry(ret->status.entry);
581         return (PyObject *)ret;
582 }
583
584 typedef struct {
585         PyObject_HEAD
586         svn_wc_adm_access_t *adm;
587         apr_pool_t *pool;
588 } AdmObject;
589
590 #define ADM_CHECK_CLOSED(adm_obj) \
591         if (adm_obj->adm == NULL) { \
592                 PyErr_SetString(PyExc_RuntimeError, "WorkingCopy instance already closed"); \
593                 return NULL; \
594         }
595
596 static PyObject *adm_init(PyTypeObject *self, PyObject *args, PyObject *kwargs)
597 {
598         PyObject *associated;
599         char *path;
600         bool write_lock=false;
601         int depth=0;
602         svn_wc_adm_access_t *parent_wc;
603         svn_error_t *err;
604         AdmObject *ret;
605         char *kwnames[] = { "associated", "path", "write_lock", "depth", NULL };
606
607         if (!PyArg_ParseTupleAndKeywords(args, kwargs, "Os|bi", kwnames,
608                         &associated, &path, &write_lock, &depth))
609                 return NULL;
610
611         ret = PyObject_New(AdmObject, &Adm_Type);
612         if (ret == NULL)
613                 return NULL;
614
615         ret->pool = Pool(NULL);
616         if (ret->pool == NULL)
617                 return NULL;
618         if (associated == Py_None) {
619                 parent_wc = NULL;
620         } else {
621                 parent_wc = ((AdmObject *)associated)->adm;
622         }
623         Py_BEGIN_ALLOW_THREADS
624         err = svn_wc_adm_open3(&ret->adm, parent_wc, 
625                                                    svn_dirent_canonicalize(path, ret->pool),
626                                                    write_lock, depth, py_cancel_check, NULL, 
627                                                    ret->pool);
628         Py_END_ALLOW_THREADS
629         
630         if (err != NULL) {
631                 handle_svn_error(err);
632                 svn_error_clear(err);
633                 Py_DECREF(ret);
634                 return NULL;
635         }
636
637         return (PyObject *)ret;
638 }
639
640 static PyObject *adm_access_path(PyObject *self)
641 {
642         AdmObject *admobj = (AdmObject *)self;
643         ADM_CHECK_CLOSED(admobj);
644         return PyString_FromString(svn_wc_adm_access_path(admobj->adm));
645 }
646
647 static PyObject *adm_locked(PyObject *self)
648 {
649         AdmObject *admobj = (AdmObject *)self;
650         ADM_CHECK_CLOSED(admobj);
651         return PyBool_FromLong(svn_wc_adm_locked(admobj->adm));
652 }
653
654 static PyObject *adm_prop_get(PyObject *self, PyObject *args)
655 {
656         char *name, *path;
657         AdmObject *admobj = (AdmObject *)self;
658         const svn_string_t *value;
659         apr_pool_t *temp_pool;
660         PyObject *ret;
661
662         if (!PyArg_ParseTuple(args, "ss", &name, &path))
663                 return NULL;
664
665         ADM_CHECK_CLOSED(admobj);
666
667         temp_pool = Pool(NULL);
668         if (temp_pool == NULL)
669                 return NULL;
670         RUN_SVN_WITH_POOL(temp_pool, svn_wc_prop_get(&value, name, path, admobj->adm, temp_pool));
671         if (value == NULL || value->data == NULL) {
672                 ret = Py_None;
673                 Py_INCREF(ret);
674         } else {
675                 ret = PyString_FromStringAndSize(value->data, value->len);
676         }
677         apr_pool_destroy(temp_pool);
678         return ret;
679 }
680
681 static PyObject *adm_prop_set(PyObject *self, PyObject *args)
682 {
683         char *name, *value, *path; 
684         AdmObject *admobj = (AdmObject *)self;
685         bool skip_checks=false;
686         apr_pool_t *temp_pool;
687         int vallen;
688         svn_string_t *cvalue;
689         PyObject *notify_func = Py_None;
690
691         if (!PyArg_ParseTuple(args, "sz#s|bO", &name, &value, &vallen, &path, &skip_checks,
692                                                   &notify_func))
693                 return NULL;
694
695         ADM_CHECK_CLOSED(admobj);
696
697         temp_pool = Pool(NULL);
698         if (temp_pool == NULL)
699                 return NULL;
700         if (value == NULL) {
701                 cvalue = NULL;
702         } else {
703                 cvalue = svn_string_ncreate(value, vallen, temp_pool);
704         }
705 #if ONLY_SINCE_SVN(1, 6)
706         RUN_SVN_WITH_POOL(temp_pool, svn_wc_prop_set3(name, cvalue, path, admobj->adm, 
707                                 skip_checks, py_wc_notify_func, (void *)notify_func, 
708                                 temp_pool));
709 #else
710         RUN_SVN_WITH_POOL(temp_pool, svn_wc_prop_set2(name, cvalue, path, admobj->adm, 
711                                 skip_checks, temp_pool));
712 #endif
713         apr_pool_destroy(temp_pool);
714
715         Py_RETURN_NONE;
716 }
717
718 static PyObject *adm_entries_read(PyObject *self, PyObject *args)
719 {
720         apr_hash_t *entries;
721         AdmObject *admobj = (AdmObject *)self;
722         apr_pool_t *temp_pool;
723         bool show_hidden=false;
724         apr_hash_index_t *idx;
725         const char *key;
726         apr_ssize_t klen;
727         svn_wc_entry_t *entry;
728         PyObject *py_entries, *obj;
729
730         if (!PyArg_ParseTuple(args, "|b", &show_hidden))
731                 return NULL;
732
733         ADM_CHECK_CLOSED(admobj);
734
735         temp_pool = Pool(NULL);
736         if (temp_pool == NULL)
737                 return NULL;
738         RUN_SVN_WITH_POOL(temp_pool, svn_wc_entries_read(&entries, admobj->adm, 
739                                  show_hidden, temp_pool));
740         py_entries = PyDict_New();
741         if (py_entries == NULL) {
742                 apr_pool_destroy(temp_pool);
743                 return NULL;
744         }
745         idx = apr_hash_first(temp_pool, entries);
746         while (idx != NULL) {
747                 apr_hash_this(idx, (const void **)&key, &klen, (void **)&entry);
748                 if (entry == NULL) {
749                         obj = Py_None;
750                         Py_INCREF(obj);
751                 } else {
752                         obj = py_entry(entry);
753                 }
754                 PyDict_SetItemString(py_entries, key, obj);
755                 Py_DECREF(obj);
756                 idx = apr_hash_next(idx);
757         }
758         apr_pool_destroy(temp_pool);
759         return py_entries;
760 }
761
762 static PyObject *adm_walk_entries(PyObject *self, PyObject *args)
763 {
764         char *path;
765         PyObject *callbacks; 
766         bool show_hidden=false;
767         apr_pool_t *temp_pool;
768         AdmObject *admobj = (AdmObject *)self;
769         svn_depth_t depth = svn_depth_infinity;
770
771         if (!PyArg_ParseTuple(args, "sO|bi", &path, &callbacks, &show_hidden, &depth))
772                 return NULL;
773
774         ADM_CHECK_CLOSED(admobj);
775
776         temp_pool = Pool(NULL);
777         if (temp_pool == NULL)
778                 return NULL;
779 #if ONLY_SINCE_SVN(1, 5)
780         RUN_SVN_WITH_POOL(temp_pool, svn_wc_walk_entries3(
781                           svn_dirent_canonicalize(path, temp_pool), admobj->adm, 
782                                 &py_wc_entry_callbacks2, (void *)callbacks,
783                                 depth, show_hidden, py_cancel_check, NULL,
784                                 temp_pool));
785 #else
786         if (depth != svn_depth_infinity) {
787                 PyErr_SetString(PyExc_NotImplementedError,
788                                                 "depth != infinity not supported for svn < 1.5");
789                 apr_pool_destroy(temp_pool);
790                 return NULL;
791         }
792         RUN_SVN_WITH_POOL(temp_pool, svn_wc_walk_entries2(
793                           svn_dirent_canonicalize(path, temp_pool), admobj->adm, 
794                                 &py_wc_entry_callbacks, (void *)callbacks,
795                                 show_hidden, py_cancel_check, NULL,
796                                 temp_pool));
797 #endif
798         apr_pool_destroy(temp_pool);
799
800         Py_RETURN_NONE;
801 }
802
803 static PyObject *adm_entry(PyObject *self, PyObject *args)
804 {
805         char *path;
806         bool show_hidden=false;
807         apr_pool_t *temp_pool;
808         AdmObject *admobj = (AdmObject *)self;
809         const svn_wc_entry_t *entry;
810         PyObject *ret;
811
812         if (!PyArg_ParseTuple(args, "s|b", &path, &show_hidden))
813                 return NULL;
814
815         ADM_CHECK_CLOSED(admobj);
816
817         temp_pool = Pool(NULL);
818         if (temp_pool == NULL)
819                 return NULL;
820         RUN_SVN_WITH_POOL(temp_pool, svn_wc_entry(&entry, svn_dirent_canonicalize(path, temp_pool), admobj->adm, show_hidden, temp_pool));
821
822         if (entry == NULL) {
823                 PyErr_Format(PyExc_KeyError, "No such entry '%s'", path);
824                 ret = NULL;
825         } else  {
826                 ret = py_entry(entry);
827         }
828
829         apr_pool_destroy(temp_pool);
830         return ret;
831 }
832
833 static PyObject *adm_get_prop_diffs(PyObject *self, PyObject *args)
834 {
835         char *path;
836         apr_pool_t *temp_pool;
837         apr_array_header_t *propchanges;
838         apr_hash_t *original_props;
839         AdmObject *admobj = (AdmObject *)self;
840         svn_prop_t el;
841         int i;
842         PyObject *py_propchanges, *py_orig_props, *pyval;
843
844         if (!PyArg_ParseTuple(args, "s", &path))
845                 return NULL;
846
847         ADM_CHECK_CLOSED(admobj);
848
849         temp_pool = Pool(NULL);
850         if (temp_pool == NULL)
851                 return NULL;
852         RUN_SVN_WITH_POOL(temp_pool, svn_wc_get_prop_diffs(&propchanges, &original_props,
853                                 svn_dirent_canonicalize(path, temp_pool), admobj->adm, temp_pool));
854         py_propchanges = PyList_New(propchanges->nelts);
855         if (py_propchanges == NULL) {
856                 apr_pool_destroy(temp_pool);
857                 return NULL;
858         }
859         for (i = 0; i < propchanges->nelts; i++) {
860                 el = APR_ARRAY_IDX(propchanges, i, svn_prop_t);
861                 if (el.value != NULL)
862                         pyval = Py_BuildValue("(sz#)", el.name, el.value->data, el.value->len);
863                 else
864                         pyval = Py_BuildValue("(sO)", el.name, Py_None);
865                 if (pyval == NULL) {
866                         apr_pool_destroy(temp_pool);
867                         Py_DECREF(py_propchanges);
868                         return NULL;
869                 }
870                 if (PyList_SetItem(py_propchanges, i, pyval) != 0) {
871                         Py_DECREF(py_propchanges);
872                         apr_pool_destroy(temp_pool);
873                         return NULL;
874                 }
875         }
876         py_orig_props = prop_hash_to_dict(original_props);
877         apr_pool_destroy(temp_pool);
878         if (py_orig_props == NULL) {
879                 Py_DECREF(py_propchanges);
880                 return NULL;
881         }
882         return Py_BuildValue("(NN)", py_propchanges, py_orig_props);
883 }
884
885 static PyObject *adm_add(PyObject *self, PyObject *args, PyObject *kwargs)
886 {
887         char *path, *copyfrom_url=NULL;
888         svn_revnum_t copyfrom_rev=-1; 
889         char *kwnames[] = { "path", "copyfrom_url", "copyfrom_rev", "notify_func", "depth", NULL };
890         PyObject *notify_func=Py_None;
891         AdmObject *admobj = (AdmObject *)self;
892         apr_pool_t *temp_pool;
893         svn_depth_t depth = svn_depth_infinity;
894
895         if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|zlOi", kwnames, &path,
896                         &copyfrom_url, &copyfrom_rev, &notify_func, &depth))
897                 return NULL;
898
899         ADM_CHECK_CLOSED(admobj);
900
901         temp_pool = Pool(NULL);
902         if (temp_pool == NULL)
903                 return NULL;
904
905 #if ONLY_SINCE_SVN(1, 6)
906         RUN_SVN_WITH_POOL(temp_pool, svn_wc_add3(
907                                                    svn_dirent_canonicalize(path, temp_pool), admobj->adm,
908                                                    depth, svn_uri_canonicalize(copyfrom_url, temp_pool), 
909                                                         copyfrom_rev, py_cancel_check, NULL,
910                                                         py_wc_notify_func, 
911                                                         (void *)notify_func, 
912                                                         temp_pool));
913 #else
914         if (depth != svn_depth_infinity) {
915                 PyErr_SetString(PyExc_NotImplementedError, "depth != infinity not supported on svn < 1.6");
916                 apr_pool_destroy(temp_pool);
917                 return NULL;
918         }
919         RUN_SVN_WITH_POOL(temp_pool, svn_wc_add2(
920                                                    svn_dirent_canonicalize(path, temp_pool), admobj->adm, copyfrom_url, 
921                                                         copyfrom_rev, py_cancel_check, 
922                                                         py_wc_notify_func, 
923                                                         (void *)notify_func, 
924                                                         temp_pool));
925 #endif
926         apr_pool_destroy(temp_pool);
927
928         Py_RETURN_NONE;
929 }
930
931 static PyObject *adm_copy(PyObject *self, PyObject *args)
932 {
933         AdmObject *admobj = (AdmObject *)self;
934         char *src, *dst; 
935         PyObject *notify_func=Py_None;
936         apr_pool_t *temp_pool;
937
938         if (!PyArg_ParseTuple(args, "ss|O", &src, &dst, &notify_func))
939                 return NULL;
940
941         ADM_CHECK_CLOSED(admobj);
942
943         temp_pool = Pool(NULL);
944         if (temp_pool == NULL)
945                 return NULL;
946         RUN_SVN_WITH_POOL(temp_pool, svn_wc_copy2(src, admobj->adm, dst,
947                                                         py_cancel_check, NULL,
948                                                         py_wc_notify_func, (void *)notify_func, 
949                                                         temp_pool));
950         apr_pool_destroy(temp_pool);
951
952         Py_RETURN_NONE;
953 }
954
955 static PyObject *adm_delete(PyObject *self, PyObject *args, PyObject *kwargs)
956 {
957         AdmObject *admobj = (AdmObject *)self;
958         apr_pool_t *temp_pool;
959         char *kwnames[] = { "path", "notify_func", "keep_local",
960                                 NULL };
961         char *path;
962         PyObject *notify_func=Py_None;
963         bool keep_local = false;
964
965         if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|Ob:delete", kwnames, 
966                         &path, &notify_func, &keep_local))
967                 return NULL;
968
969         ADM_CHECK_CLOSED(admobj);
970
971         temp_pool = Pool(NULL);
972         if (temp_pool == NULL)
973                 return NULL;
974
975 #if ONLY_SINCE_SVN(1, 5)
976         RUN_SVN_WITH_POOL(temp_pool, svn_wc_delete3(path, admobj->adm, 
977                                                         py_cancel_check, NULL,
978                                                         py_wc_notify_func, (void *)notify_func, 
979                                                         keep_local,
980                                                         temp_pool));
981 #else
982         if (keep_local) {
983                 PyErr_SetString(PyExc_NotImplementedError, 
984                                                 "keep_local not supported on Subversion < 1.5");
985                 return NULL;
986         }
987
988         RUN_SVN_WITH_POOL(temp_pool, svn_wc_delete2(path, admobj->adm, 
989                                                         py_cancel_check, NULL,
990                                                         py_wc_notify_func, (void *)notify_func, 
991                                                         temp_pool));
992 #endif
993         apr_pool_destroy(temp_pool);
994
995         Py_RETURN_NONE;
996 }
997
998 static PyObject *adm_crawl_revisions(PyObject *self, PyObject *args, PyObject *kwargs)
999 {
1000         char *path;
1001         PyObject *reporter;
1002         bool restore_files=true, recurse=true, use_commit_times=true;
1003         PyObject *notify_func=Py_None;
1004         apr_pool_t *temp_pool;
1005         AdmObject *admobj = (AdmObject *)self;
1006         svn_wc_traversal_info_t *traversal_info;
1007         svn_boolean_t depth_compatibility_trick = FALSE;
1008         svn_boolean_t honor_depth_exclude = FALSE;
1009         char *kwnames[] = { "path", "reporter", "restore_files", "recurse", "use_commit_times", "notify_func", "depth_compatibility_trick", "honor_depth_exclude,", NULL };
1010
1011         if (!PyArg_ParseTupleAndKeywords(args, kwargs, "sO|bbbObb", kwnames, &path,
1012                         &reporter, &restore_files, &recurse, &use_commit_times,
1013                         &notify_func, &depth_compatibility_trick, &honor_depth_exclude))
1014                 return NULL;
1015
1016         ADM_CHECK_CLOSED(admobj);
1017
1018         temp_pool = Pool(NULL);
1019         if (temp_pool == NULL)
1020                 return NULL;
1021         traversal_info = svn_wc_init_traversal_info(temp_pool);
1022 #if ONLY_SINCE_SVN(1, 6)
1023         RUN_SVN_WITH_POOL(temp_pool, svn_wc_crawl_revisions4(svn_dirent_canonicalize(path, temp_pool), admobj->adm, 
1024                                 &py_ra_reporter, (void *)reporter, 
1025                                 restore_files, recurse?svn_depth_infinity:svn_depth_files,
1026                                 honor_depth_exclude,
1027                                 depth_compatibility_trick, use_commit_times, 
1028                                 py_wc_notify_func, (void *)notify_func,
1029                                 traversal_info, temp_pool));
1030 #elif ONLY_SINCE_SVN(1, 5)
1031         RUN_SVN_WITH_POOL(temp_pool, svn_wc_crawl_revisions3(svn_dirent_canonicalize(path, temp_pool), admobj->adm, 
1032                                 &py_ra_reporter, (void *)reporter, 
1033                                 restore_files, recurse?svn_depth_infinity:svn_depth_files, 
1034                                 depth_compatibility_trick, use_commit_times, 
1035                                 py_wc_notify_func, (void *)notify_func,
1036                                 traversal_info, temp_pool));
1037 #else
1038         RUN_SVN_WITH_POOL(temp_pool, svn_wc_crawl_revisions2(svn_dirent_canonicalize(path, temp_pool), admobj->adm, 
1039                                 &py_ra_reporter, (void *)reporter, 
1040                                 restore_files, recurse, use_commit_times, 
1041                                 py_wc_notify_func, (void *)notify_func,
1042                                 traversal_info, temp_pool));
1043 #endif
1044         apr_pool_destroy(temp_pool);
1045
1046         Py_RETURN_NONE;
1047 }
1048
1049 static void wc_done_handler(void *self)
1050 {
1051         AdmObject *admobj = (AdmObject *)self;
1052
1053         Py_DECREF(admobj);
1054 }
1055
1056 static PyObject *adm_get_update_editor(PyObject *self, PyObject *args)
1057 {
1058         char *target;
1059         bool use_commit_times=true, recurse=true;
1060         PyObject * notify_func=Py_None;
1061         char *diff3_cmd=NULL;
1062         const svn_delta_editor_t *editor;
1063         AdmObject *admobj = (AdmObject *)self;
1064         void *edit_baton;
1065         apr_pool_t *pool;
1066         svn_revnum_t *latest_revnum;
1067         svn_error_t *err;
1068         svn_boolean_t allow_unver_obstructions = FALSE;
1069         svn_boolean_t depth_is_sticky = FALSE;
1070
1071         if (!PyArg_ParseTuple(args, "s|bbOzbb", &target, &use_commit_times,
1072                         &recurse, &notify_func, &diff3_cmd, &depth_is_sticky,
1073                         &allow_unver_obstructions))
1074                 return NULL;
1075
1076         ADM_CHECK_CLOSED(admobj);
1077
1078         pool = Pool(NULL);
1079         if (pool == NULL)
1080                 return NULL;
1081         latest_revnum = (svn_revnum_t *)apr_palloc(pool, sizeof(svn_revnum_t));
1082         Py_BEGIN_ALLOW_THREADS
1083 #if ONLY_SINCE_SVN(1, 5)
1084         /* FIXME: Support all values of depth */
1085         /* FIXME: Support fetch_func */
1086         /* FIXME: Support conflict func */
1087         err = svn_wc_get_update_editor3(latest_revnum, admobj->adm, target, 
1088                                 use_commit_times, recurse?svn_depth_infinity:svn_depth_files,
1089                                 depth_is_sticky, allow_unver_obstructions, 
1090                                 py_wc_notify_func, (void *)notify_func, 
1091                                 py_cancel_check, NULL,
1092                                 NULL, NULL, NULL, NULL,
1093                                 diff3_cmd, NULL, &editor, &edit_baton, 
1094                                 NULL, pool);
1095 #else
1096         if (allow_unver_obstructions) {
1097                 PyErr_SetString(PyExc_NotImplementedError, 
1098                                                 "allow_unver_obstructions is not supported in svn < 1.5");
1099                 apr_pool_destroy(pool);
1100                 PyEval_RestoreThread(_save);
1101                 return NULL;
1102         }
1103         if (depth_is_sticky) {
1104                 PyErr_SetString(PyExc_NotImplementedError, 
1105                                                 "depth_is_sticky is not supported in svn < 1.5");
1106                 apr_pool_destroy(pool);
1107                 PyEval_RestoreThread(_save);
1108                 return NULL;
1109         }
1110         err = svn_wc_get_update_editor2(latest_revnum, admobj->adm, target, 
1111                                 use_commit_times, recurse, py_wc_notify_func, (void *)notify_func, 
1112                                 py_cancel_check, NULL, diff3_cmd, &editor, &edit_baton, 
1113                                 NULL, pool);
1114 #endif
1115         Py_END_ALLOW_THREADS
1116         if (err != NULL) {
1117                 handle_svn_error(err);
1118                 svn_error_clear(err);
1119                 apr_pool_destroy(pool);
1120                 return NULL;
1121         }
1122         Py_INCREF(admobj);
1123         return new_editor_object(NULL, editor, edit_baton, pool, &Editor_Type,
1124                 wc_done_handler, admobj, NULL);
1125 }
1126
1127 static bool py_dict_to_wcprop_changes(PyObject *dict, apr_pool_t *pool, apr_array_header_t **ret)
1128 {
1129         PyObject *key, *val;
1130         Py_ssize_t idx;
1131
1132         if (dict == Py_None) {
1133                 *ret = NULL;
1134                 return true;
1135         }
1136
1137         if (!PyDict_Check(dict)) {
1138                 PyErr_SetString(PyExc_TypeError, "Expected dictionary with property changes");
1139                 return false;
1140         }
1141
1142         *ret = apr_array_make(pool, PyDict_Size(dict), sizeof(char *));
1143
1144         while (PyDict_Next(dict, &idx, &key, &val)) {
1145                 svn_prop_t *prop = apr_palloc(pool, sizeof(svn_prop_t));
1146                 prop->name = py_object_to_svn_string(key, pool);
1147                 if (prop->name == NULL) {
1148                         return false;
1149                 }
1150                 if (val == Py_None) {
1151                         prop->value = NULL;
1152                 } else {
1153                         if (!PyBytes_Check(val)) {
1154                                 PyErr_SetString(PyExc_TypeError, "property values should be bytes");
1155                                 return false;
1156                         }
1157                         prop->value = svn_string_ncreate(PyBytes_AsString(val), PyBytes_Size(val), pool);
1158                 }
1159                 APR_ARRAY_PUSH(*ret, svn_prop_t *) = prop;
1160         }
1161
1162         return true;
1163 }
1164
1165 static PyObject *adm_has_binary_prop(PyObject *self, PyObject *args)
1166 {
1167         char *path;
1168         svn_boolean_t binary;
1169         AdmObject *admobj = (AdmObject *)self;
1170         apr_pool_t *temp_pool;
1171
1172         if (!PyArg_ParseTuple(args, "s", &path))
1173                 return NULL;
1174
1175         ADM_CHECK_CLOSED(admobj);
1176
1177         temp_pool = Pool(NULL);
1178         if (temp_pool == NULL)
1179                 return NULL;
1180
1181         RUN_SVN_WITH_POOL(temp_pool, svn_wc_has_binary_prop(&binary, path, admobj->adm, temp_pool));
1182
1183         apr_pool_destroy(temp_pool);
1184
1185         return PyBool_FromLong(binary);
1186 }
1187
1188 static PyObject *adm_process_committed(PyObject *self, PyObject *args, PyObject *kwargs)
1189 {
1190         char *path, *rev_date = NULL, *rev_author = NULL;
1191         bool recurse, remove_lock = false;
1192         unsigned char *digest = NULL;
1193         svn_revnum_t new_revnum;
1194         PyObject *py_wcprop_changes = Py_None;
1195         apr_array_header_t *wcprop_changes = NULL;
1196         AdmObject *admobj = (AdmObject *)self;
1197         apr_pool_t *temp_pool;
1198         int digest_len;
1199         svn_boolean_t remove_changelist = FALSE;
1200         char *kwnames[] = { "path", "recurse", "new_revnum", "rev_date", "rev_author", 
1201                                                 "wcprop_changes", "remove_lock", "digest", "remove_changelist", NULL };
1202
1203         if (!PyArg_ParseTupleAndKeywords(args, kwargs, "sblzz|Obz#b", kwnames, 
1204                                                                          &path, &recurse, &new_revnum, &rev_date,
1205                                                                          &rev_author, &py_wcprop_changes, 
1206                                                                          &remove_lock, &digest, &digest_len, &remove_changelist))
1207                 return NULL;
1208
1209         PyErr_WarnEx(PyExc_DeprecationWarning, "process_committed is deprecated. Use process_committed_queue instead.", 2);
1210
1211
1212         ADM_CHECK_CLOSED(admobj);
1213
1214         temp_pool = Pool(NULL);
1215         if (temp_pool == NULL)
1216                 return NULL;
1217
1218         if (!py_dict_to_wcprop_changes(py_wcprop_changes, temp_pool, &wcprop_changes)) {
1219                 apr_pool_destroy(temp_pool);
1220                 return NULL;
1221         }
1222
1223 #if ONLY_SINCE_SVN(1, 6)
1224         RUN_SVN_WITH_POOL(temp_pool, svn_wc_process_committed4(
1225                 svn_dirent_canonicalize(path, temp_pool), admobj->adm, recurse, new_revnum, 
1226                         rev_date, rev_author, wcprop_changes, 
1227                         remove_lock, remove_changelist, digest, temp_pool));
1228 #else
1229         if (remove_changelist) {
1230                 PyErr_SetString(PyExc_NotImplementedError, "remove_changelist only supported in svn < 1.6");
1231                 apr_pool_destroy(temp_pool);
1232                 return NULL;
1233         }
1234         RUN_SVN_WITH_POOL(temp_pool, svn_wc_process_committed3(svn_dirent_canonicalize(path, temp_pool), admobj->adm, recurse, new_revnum, 
1235                                                                                                                    rev_date, rev_author, wcprop_changes, 
1236                                                                                                                    remove_lock, digest, temp_pool));
1237 #endif
1238
1239         apr_pool_destroy(temp_pool);
1240
1241         Py_RETURN_NONE;
1242 }
1243
1244 static PyObject *adm_close(PyObject *self)
1245 {
1246         AdmObject *admobj = (AdmObject *)self;
1247         if (admobj->adm != NULL) {
1248 #if ONLY_SINCE_SVN(1, 6)
1249                 apr_pool_t *temp_pool = Pool(NULL);
1250                 Py_BEGIN_ALLOW_THREADS
1251                 svn_wc_adm_close2(admobj->adm, temp_pool);
1252                 apr_pool_destroy(temp_pool);
1253 #else
1254                 Py_BEGIN_ALLOW_THREADS
1255                 svn_wc_adm_close(admobj->adm);
1256 #endif
1257                 Py_END_ALLOW_THREADS
1258                 admobj->adm = NULL;
1259         }
1260
1261         Py_RETURN_NONE;
1262 }
1263
1264 static void adm_dealloc(PyObject *self)
1265 {
1266         apr_pool_destroy(((AdmObject *)self)->pool);
1267         PyObject_Del(self);
1268 }
1269
1270 static PyObject *adm_repr(PyObject *self)
1271 {
1272         AdmObject *admobj = (AdmObject *)self;
1273
1274         if (admobj->adm == NULL) {
1275                 return PyString_FromFormat("<wc.WorkingCopy (closed) at 0x%p>", admobj);
1276         } else {
1277                 return PyString_FromFormat("<wc.WorkingCopy at '%s'>", 
1278                                                                    svn_wc_adm_access_path(admobj->adm));
1279         }
1280 }
1281
1282 static PyObject *adm_remove_lock(PyObject *self, PyObject *args)
1283 {
1284         char *path;
1285         AdmObject *admobj = (AdmObject *)self;
1286         apr_pool_t *temp_pool;
1287
1288         if (!PyArg_ParseTuple(args, "s", &path))
1289                 return NULL;
1290
1291         ADM_CHECK_CLOSED(admobj);
1292
1293         temp_pool = Pool(NULL);
1294         if (temp_pool == NULL)
1295                 return NULL;
1296
1297         RUN_SVN_WITH_POOL(temp_pool, svn_wc_remove_lock(path, admobj->adm, temp_pool))
1298
1299         apr_pool_destroy(temp_pool);
1300
1301         Py_RETURN_NONE;
1302 }
1303
1304 static PyObject *get_ancestry(PyObject *self, PyObject *args)
1305 {
1306         char *path;
1307         char *url;
1308         svn_revnum_t rev;
1309         apr_pool_t *temp_pool;
1310         AdmObject *admobj = (AdmObject *)self;
1311
1312         if (!PyArg_ParseTuple(args, "s", &path))
1313                 return NULL;
1314
1315         ADM_CHECK_CLOSED(admobj);
1316
1317         temp_pool = Pool(NULL);
1318         if (temp_pool == NULL)
1319                 return NULL;
1320
1321         RUN_SVN_WITH_POOL(temp_pool, svn_wc_get_ancestry(&url, &rev, path, admobj->adm, temp_pool));
1322
1323         apr_pool_destroy(temp_pool);
1324
1325         return Py_BuildValue("(si)", url, rev);
1326 }
1327
1328 static PyObject *maybe_set_repos_root(PyObject *self, PyObject *args)
1329 {
1330         char *path, *repos;
1331         apr_pool_t *temp_pool;
1332         AdmObject *admobj = (AdmObject *)self;
1333
1334         if (!PyArg_ParseTuple(args, "ss", &path, &repos))
1335                 return NULL;
1336
1337         ADM_CHECK_CLOSED(admobj);
1338
1339         temp_pool = Pool(NULL);
1340         if (temp_pool == NULL)
1341                 return NULL;
1342
1343         RUN_SVN_WITH_POOL(temp_pool, svn_wc_maybe_set_repos_root(admobj->adm, path, repos, temp_pool));
1344
1345         Py_RETURN_NONE;
1346 }
1347
1348 static PyObject *add_repos_file(PyObject *self, PyObject *args, PyObject *kwargs)
1349 {
1350         char *kwnames[] = { "dst_path", "new_base_contents", "new_contents",
1351                 "new_base_props", "new_props", "copyfrom_url", "copyfrom_rev",
1352                 "notify", NULL };
1353         AdmObject *admobj = (AdmObject *)self;
1354         apr_pool_t *temp_pool;
1355         char *dst_path, *copyfrom_url = NULL;
1356         svn_revnum_t copyfrom_rev = -1;
1357         PyObject *py_new_base_contents, *py_new_contents, *py_new_base_props,
1358                          *py_new_props, *notify = Py_None;
1359         svn_stream_t *new_contents, *new_base_contents;
1360         apr_hash_t *new_props, *new_base_props;
1361
1362         if (!PyArg_ParseTupleAndKeywords(args, kwargs, "sOOOO|zlO", kwnames,
1363                         &dst_path, &py_new_base_contents, &py_new_contents, &py_new_base_props, 
1364                         &py_new_props, &copyfrom_url, &copyfrom_rev, &notify))
1365                 return NULL;
1366
1367         ADM_CHECK_CLOSED(admobj);
1368
1369         temp_pool = Pool(NULL);
1370         if (temp_pool == NULL)
1371                 return NULL;
1372
1373         new_base_props = prop_dict_to_hash(temp_pool, py_new_base_props);
1374
1375         new_props = prop_dict_to_hash(temp_pool, py_new_props);
1376
1377         new_base_contents = new_py_stream(temp_pool, py_new_base_contents);
1378
1379         new_contents = new_py_stream(temp_pool, py_new_contents);
1380
1381 #if ONLY_SINCE_SVN(1, 6)
1382         RUN_SVN_WITH_POOL(temp_pool, svn_wc_add_repos_file3(dst_path, admobj->adm,
1383                                                    new_base_contents,
1384                                                    new_contents,
1385                                                    new_base_props,
1386                                                    new_props,
1387                                                    copyfrom_url, copyfrom_rev,
1388                                                    py_cancel_check, NULL,
1389                                                    py_wc_notify_func, notify, temp_pool));
1390 #else
1391         PyErr_SetString(PyExc_NotImplementedError,
1392                                         "add_repos_file3 not supported on svn < 1.6");
1393         apr_pool_destroy(temp_pool);
1394 #endif
1395
1396         apr_pool_destroy(temp_pool);
1397
1398         Py_RETURN_NONE;
1399 }
1400
1401 static PyObject *mark_missing_deleted(PyObject *self, PyObject *args)
1402 {
1403         char *path;
1404         AdmObject *admobj = (AdmObject *)self;
1405         apr_pool_t *temp_pool;
1406
1407         if (!PyArg_ParseTuple(args, "s", &path))
1408                 return NULL;
1409
1410         ADM_CHECK_CLOSED(admobj);
1411
1412         temp_pool = Pool(NULL);
1413         if (temp_pool == NULL)
1414                 return NULL;
1415
1416         RUN_SVN_WITH_POOL(temp_pool, svn_wc_mark_missing_deleted(path, admobj->adm, temp_pool));
1417
1418         apr_pool_destroy(temp_pool);
1419
1420         Py_RETURN_NONE;
1421 }
1422
1423 static PyObject *remove_from_revision_control(PyObject *self, PyObject *args)
1424 {
1425         char *name;
1426         svn_boolean_t destroy_wf = FALSE, instant_error = FALSE;
1427         AdmObject *admobj = (AdmObject *)self;
1428         apr_pool_t *temp_pool;
1429
1430         if (!PyArg_ParseTuple(args, "s|bb", &name, &destroy_wf, &instant_error))
1431                 return NULL;
1432
1433         ADM_CHECK_CLOSED(admobj);
1434
1435         temp_pool = Pool(NULL);
1436         if (temp_pool == NULL)
1437                 return NULL;
1438
1439         RUN_SVN_WITH_POOL(temp_pool,
1440                 svn_wc_remove_from_revision_control(admobj->adm, name,
1441                         destroy_wf, instant_error, py_cancel_check, NULL, temp_pool));
1442
1443         apr_pool_destroy(temp_pool);
1444
1445         Py_RETURN_NONE;
1446 }
1447
1448 #if ONLY_SINCE_SVN(1, 6)
1449 static svn_error_t *wc_validator3(void *baton, const char *uuid, const char *url, const char *root_url, apr_pool_t *pool)
1450 {
1451         PyObject *py_validator = baton, *ret;
1452
1453         if (py_validator == Py_None) {
1454                 return NULL;
1455         }
1456
1457         ret = PyObject_CallFunction(py_validator, "sss", uuid, url, root_url);
1458         if (ret == NULL) {
1459                 return py_svn_error();
1460         }
1461
1462         Py_DECREF(ret);
1463
1464         return NULL;
1465 }
1466
1467 #else
1468
1469 static svn_error_t *wc_validator2(void *baton, const char *uuid, const char *url, svn_boolean_t root, apr_pool_t *pool)
1470 {
1471         PyObject *py_validator = baton, *ret;
1472
1473         if (py_validator == Py_None) {
1474                 return NULL;
1475         }
1476
1477         ret = PyObject_CallFunction(py_validator, "ssO", uuid, url, Py_None);
1478         if (ret == NULL) {
1479                 return py_svn_error();
1480         }
1481
1482         Py_DECREF(ret);
1483
1484         return NULL;
1485 }
1486
1487 #endif
1488
1489 static PyObject *relocate(PyObject *self, PyObject *args)
1490 {
1491         char *path, *from, *to;
1492         AdmObject *admobj = (AdmObject *)self;
1493         apr_pool_t *temp_pool;
1494         svn_boolean_t recurse = TRUE;
1495         PyObject *py_validator = Py_None;
1496
1497         if (!PyArg_ParseTuple(args, "sss|bO:relocate", &path, &from, &to, &recurse,
1498                         &py_validator))
1499                 return NULL;
1500
1501         ADM_CHECK_CLOSED(admobj);
1502
1503         temp_pool = Pool(NULL);
1504         if (temp_pool == NULL)
1505                 return NULL;
1506
1507 #if ONLY_SINCE_SVN(1, 6)
1508         RUN_SVN_WITH_POOL(temp_pool, svn_wc_relocate3(path, admobj->adm, from, to, recurse, wc_validator3, py_validator, temp_pool));
1509 #else
1510         RUN_SVN_WITH_POOL(temp_pool, svn_wc_relocate2(path, admobj->adm, from, to, recurse, wc_validator2, py_validator, temp_pool));
1511 #endif
1512
1513         apr_pool_destroy(temp_pool);
1514
1515         Py_RETURN_NONE;
1516 }
1517
1518 static PyObject *crop_tree(PyObject *self, PyObject *args)
1519 {
1520         char *target;
1521         svn_depth_t depth;
1522         PyObject *notify;
1523         apr_pool_t *temp_pool;
1524         AdmObject *admobj = (AdmObject *)self;
1525
1526         if (!PyArg_ParseTuple(args, "si|O", &target, &depth, &notify))
1527                 return NULL;
1528
1529         ADM_CHECK_CLOSED(admobj);
1530
1531 #if ONLY_SINCE_SVN(1, 6)
1532         temp_pool = Pool(NULL);
1533         if (temp_pool == NULL)
1534                 return NULL;
1535
1536         RUN_SVN_WITH_POOL(temp_pool, svn_wc_crop_tree(admobj->adm, 
1537                 target, depth, py_wc_notify_func, notify,
1538                 py_cancel_check, NULL, temp_pool));
1539
1540         apr_pool_destroy(temp_pool);
1541
1542         Py_RETURN_NONE;
1543 #else
1544         PyErr_SetString(PyExc_NotImplementedError, 
1545                                         "crop_tree only available on subversion < 1.6");
1546         return NULL;
1547 #endif
1548 }
1549
1550 static PyObject *translated_stream(PyObject *self, PyObject *args)
1551 {
1552         char *path, *versioned_file;
1553         StreamObject *ret;
1554         svn_stream_t *stream;
1555         AdmObject *admobj = (AdmObject *)self;
1556         apr_pool_t *stream_pool;
1557         int flags;
1558
1559         if (!PyArg_ParseTuple(args, "ssi", &path, &versioned_file, &flags))
1560                 return NULL;
1561
1562         ADM_CHECK_CLOSED(admobj);
1563
1564 #if ONLY_SINCE_SVN(1, 5)
1565         stream_pool = Pool(NULL);
1566         if (stream_pool == NULL)
1567                 return NULL;
1568
1569         RUN_SVN_WITH_POOL(stream_pool,
1570                 svn_wc_translated_stream(&stream, path, versioned_file, admobj->adm, 
1571                         flags, stream_pool));
1572
1573         ret = PyObject_New(StreamObject, &Stream_Type);
1574         if (ret == NULL)
1575                 return NULL;
1576
1577         ret->pool = stream_pool;
1578         ret->closed = FALSE;
1579         ret->stream = stream;
1580
1581         return (PyObject *)ret;
1582 #else
1583         PyErr_SetString(PyExc_NotImplementedError,
1584                 "translated_stream() is only available on Subversion >= 1.5");
1585         return NULL;
1586 #endif
1587 }
1588
1589 static PyObject *adm_text_modified(PyObject *self, PyObject *args)
1590 {
1591         char *path;
1592         svn_boolean_t force_comparison = FALSE;
1593         apr_pool_t *temp_pool;
1594         svn_boolean_t ret;
1595         AdmObject *admobj = (AdmObject *)self;
1596
1597         if (!PyArg_ParseTuple(args, "s|b", &path, &force_comparison))
1598                 return NULL;
1599
1600         ADM_CHECK_CLOSED(admobj);
1601
1602         temp_pool = Pool(NULL);
1603         if (temp_pool == NULL)
1604                 return NULL;
1605
1606         RUN_SVN_WITH_POOL(temp_pool,
1607                   svn_wc_text_modified_p(&ret, path, force_comparison, admobj->adm, 
1608                         temp_pool));
1609
1610         apr_pool_destroy(temp_pool);
1611
1612         return PyBool_FromLong(ret);
1613 }
1614
1615 static PyObject *adm_props_modified(PyObject *self, PyObject *args)
1616 {
1617         char *path;
1618         apr_pool_t *temp_pool;
1619         svn_boolean_t ret;
1620         AdmObject *admobj = (AdmObject *)self;
1621
1622         if (!PyArg_ParseTuple(args, "s", &path))
1623                 return NULL;
1624
1625         ADM_CHECK_CLOSED(admobj);
1626
1627         temp_pool = Pool(NULL);
1628         if (temp_pool == NULL)
1629                 return NULL;
1630
1631         RUN_SVN_WITH_POOL(temp_pool,
1632                   svn_wc_props_modified_p(&ret, path, admobj->adm, temp_pool));
1633
1634         apr_pool_destroy(temp_pool);
1635
1636         return PyBool_FromLong(ret);
1637 }
1638
1639 static PyObject *adm_process_committed_queue(PyObject *self, PyObject *args)
1640 {
1641         apr_pool_t *temp_pool;
1642         AdmObject *admobj = (AdmObject *)self;
1643         svn_revnum_t revnum;
1644         char *date, *author;
1645         CommittedQueueObject *py_queue;
1646
1647         if (!PyArg_ParseTuple(args, "O!lss", &CommittedQueue_Type, &py_queue,
1648                         &revnum, &date, &author))
1649                 return NULL;
1650
1651         ADM_CHECK_CLOSED(admobj);
1652
1653         temp_pool = Pool(NULL);
1654         if (temp_pool == NULL)
1655                 return NULL;
1656
1657 #if ONLY_SINCE_SVN(1, 5)
1658         RUN_SVN_WITH_POOL(temp_pool, svn_wc_process_committed_queue(py_queue->queue, admobj->adm, revnum, date, author, temp_pool));
1659 #else
1660         {
1661                 int i;
1662                 for (i = 0; i < py_queue->queue->queue->nelts; i++) {
1663                         committed_queue_item_t *cqi = APR_ARRAY_IDX(py_queue->queue->queue, i,
1664                                                                                                                 committed_queue_item_t *);
1665
1666                         RUN_SVN_WITH_POOL(temp_pool, svn_wc_process_committed3(cqi->path, admobj->adm,
1667                                    cqi->recurse, revnum, date, author, cqi->wcprop_changes,
1668                                    cqi->remove_lock, cqi->digest, temp_pool));
1669                 }
1670         }
1671 #endif
1672         apr_pool_destroy(temp_pool);
1673
1674         Py_RETURN_NONE;
1675 }
1676
1677 static PyObject *get_actual_target(PyObject *self, PyObject *args)
1678 {
1679         char *path;
1680         const char *anchor = NULL, *target = NULL;
1681         apr_pool_t *temp_pool;
1682         PyObject *ret;
1683
1684         if (!PyArg_ParseTuple(args, "s", &path))
1685                 return NULL;
1686
1687         temp_pool = Pool(NULL);
1688         if (temp_pool == NULL)
1689                 return NULL;
1690
1691         RUN_SVN_WITH_POOL(temp_pool,
1692                   svn_wc_get_actual_target(svn_dirent_canonicalize(path, temp_pool),
1693                                                                    &anchor, &target, temp_pool));
1694
1695         ret = Py_BuildValue("(ss)", anchor, target);
1696
1697         apr_pool_destroy(temp_pool);
1698
1699         return ret;
1700 }
1701
1702 static PyObject *is_wc_root(PyObject *self, PyObject *args)
1703 {
1704         char *path;
1705         svn_boolean_t wc_root;
1706         apr_pool_t *temp_pool;
1707         AdmObject *admobj = (AdmObject *)self;
1708
1709         if (!PyArg_ParseTuple(args, "s", &path))
1710                 return NULL;
1711
1712         ADM_CHECK_CLOSED(admobj);
1713
1714         temp_pool = Pool(NULL);
1715         if (temp_pool == NULL)
1716                 return NULL;
1717
1718         RUN_SVN_WITH_POOL(temp_pool,
1719                   svn_wc_is_wc_root(&wc_root, path, admobj->adm, temp_pool));
1720
1721         apr_pool_destroy(temp_pool);
1722
1723         return PyBool_FromLong(wc_root);
1724 }
1725
1726 static PyObject *transmit_text_deltas(PyObject *self, PyObject *args)
1727 {
1728         char *path;
1729         const char *tempfile;
1730         svn_boolean_t fulltext;
1731         PyObject *editor_obj, *py_digest;
1732         unsigned char digest[APR_MD5_DIGESTSIZE];
1733         apr_pool_t *temp_pool;
1734         AdmObject *admobj = (AdmObject *)self;
1735         PyObject *ret;
1736
1737         if (!PyArg_ParseTuple(args, "sbO", &path, &fulltext, &editor_obj))
1738                 return NULL;
1739
1740         ADM_CHECK_CLOSED(admobj);
1741
1742         temp_pool = Pool(NULL);
1743         if (temp_pool == NULL)
1744                 return NULL;
1745
1746         Py_INCREF(editor_obj);
1747
1748         RUN_SVN_WITH_POOL(temp_pool,
1749                 svn_wc_transmit_text_deltas2(&tempfile, digest,
1750                         svn_dirent_canonicalize(path, temp_pool), admobj->adm, fulltext,
1751                         &py_editor, editor_obj, temp_pool));
1752
1753         py_digest = PyString_FromStringAndSize((char *)digest, APR_MD5_DIGESTSIZE);
1754         if (py_digest == NULL) {
1755                 apr_pool_destroy(temp_pool);
1756                 return NULL;
1757         }
1758
1759         ret = Py_BuildValue("sN", tempfile, py_digest);
1760         if (ret == NULL) {
1761                 apr_pool_destroy(temp_pool);
1762                 return NULL;
1763         }
1764
1765         apr_pool_destroy(temp_pool);
1766
1767         return ret;
1768 }
1769
1770 static PyObject *transmit_prop_deltas(PyObject *self, PyObject *args)
1771 {
1772         char *path;
1773         PyObject *editor_obj;
1774         apr_pool_t *temp_pool;
1775         AdmObject *admobj = (AdmObject *)self;
1776         EntryObject *py_entry;
1777
1778         if (!PyArg_ParseTuple(args, "sO!O", &path, &Entry_Type, &py_entry, &editor_obj))
1779                 return NULL;
1780
1781         ADM_CHECK_CLOSED(admobj);
1782
1783         temp_pool = Pool(NULL);
1784         if (temp_pool == NULL)
1785                 return NULL;
1786
1787         Py_INCREF(editor_obj);
1788
1789         RUN_SVN_WITH_POOL(temp_pool,
1790                 svn_wc_transmit_prop_deltas(svn_dirent_canonicalize(path, temp_pool),
1791                         admobj->adm, &(py_entry->entry), &py_editor, editor_obj, NULL, temp_pool));
1792
1793         apr_pool_destroy(temp_pool);
1794
1795         Py_RETURN_NONE;
1796 }
1797
1798 static PyObject *retrieve(PyObject *self, PyObject *args)
1799 {
1800         char *path;
1801         svn_wc_adm_access_t *result;
1802         AdmObject *admobj = (AdmObject *)self, *ret;
1803         apr_pool_t *pool;
1804
1805         if (!PyArg_ParseTuple(args, "s", &path))
1806                 return NULL;
1807
1808         ADM_CHECK_CLOSED(admobj);
1809
1810         pool = Pool(NULL);
1811         if (pool == NULL)
1812                 return NULL;
1813
1814         RUN_SVN_WITH_POOL(pool, svn_wc_adm_retrieve(&result, admobj->adm, 
1815                 svn_dirent_canonicalize(path, pool), pool));
1816
1817         ret = PyObject_New(AdmObject, &Adm_Type);
1818         if (ret == NULL)
1819                 return NULL;
1820
1821         ret->pool = pool;
1822         ret->adm = result;
1823
1824         return (PyObject *)ret;
1825 }
1826
1827 static PyObject *probe_retrieve(PyObject *self, PyObject *args)
1828 {
1829         char *path;
1830         svn_wc_adm_access_t *result;
1831         AdmObject *admobj = (AdmObject *)self, *ret;
1832         apr_pool_t *pool;
1833
1834         if (!PyArg_ParseTuple(args, "s", &path))
1835                 return NULL;
1836
1837         ADM_CHECK_CLOSED(admobj);
1838
1839         pool = Pool(NULL);
1840         if (pool == NULL)
1841                 return NULL;
1842
1843         RUN_SVN_WITH_POOL(pool, svn_wc_adm_probe_retrieve(&result, admobj->adm, 
1844                 svn_dirent_canonicalize(path, pool), pool));
1845
1846         ret = PyObject_New(AdmObject, &Adm_Type);
1847         if (ret == NULL)
1848                 return NULL;
1849
1850         ret->pool = pool;
1851         ret->adm = result;
1852
1853         return (PyObject *)ret;
1854 }
1855
1856 static PyObject *probe_try(PyObject *self, PyObject *args)
1857 {
1858         char *path;
1859         svn_wc_adm_access_t *result = NULL;
1860         AdmObject *admobj = (AdmObject *)self, *ret;
1861         apr_pool_t *pool;
1862         int levels_to_lock = -1;
1863         svn_boolean_t writelock = FALSE;
1864
1865         if (!PyArg_ParseTuple(args, "s|bi", &path, &writelock, &levels_to_lock))
1866                 return NULL;
1867
1868         ADM_CHECK_CLOSED(admobj);
1869
1870         pool = Pool(NULL);
1871         if (pool == NULL)
1872                 return NULL;
1873
1874         RUN_SVN_WITH_POOL(pool, svn_wc_adm_probe_try3(&result, admobj->adm, 
1875                 svn_dirent_canonicalize(path, pool), writelock, levels_to_lock,
1876                 py_cancel_check, NULL, pool));
1877
1878         if (result == NULL) {
1879                 apr_pool_destroy(pool);
1880                 Py_RETURN_NONE;
1881         }
1882
1883         ret = PyObject_New(AdmObject, &Adm_Type);
1884         if (ret == NULL)
1885                 return NULL;
1886
1887         ret->pool = pool;
1888         ret->adm = result;
1889
1890         return (PyObject *)ret;
1891 }
1892
1893 static PyObject *resolved_conflict(PyObject *self, PyObject *args)
1894 {
1895         AdmObject *admobj = (AdmObject *)self;
1896         apr_pool_t *temp_pool;
1897         svn_boolean_t resolve_props, resolve_tree, resolve_text;
1898         int depth;
1899 #if ONLY_SINCE_SVN(1, 5)
1900         svn_wc_conflict_choice_t conflict_choice;
1901 #else
1902         int conflict_choice;
1903 #endif
1904         PyObject *notify_func = Py_None;
1905         char *path;
1906
1907         if (!PyArg_ParseTuple(args, "sbbbii|O", &path, &resolve_text,
1908                                                   &resolve_props, &resolve_tree, &depth,
1909                                                   &conflict_choice, &notify_func))
1910                 return NULL;
1911
1912         ADM_CHECK_CLOSED(admobj);
1913
1914         temp_pool = Pool(NULL);
1915         if (temp_pool == NULL)
1916                 return NULL;
1917
1918 #if ONLY_SINCE_SVN(1, 6)
1919         RUN_SVN_WITH_POOL(temp_pool,
1920                   svn_wc_resolved_conflict4(path, admobj->adm, resolve_text, 
1921                                                                                 resolve_props, resolve_tree, depth,
1922                                                                                 conflict_choice, py_wc_notify_func,
1923                                                                            (void *)notify_func, py_cancel_check,
1924                                                                            NULL, temp_pool));
1925 #elif ONLY_SINCE_SVN(1, 5)
1926         if (resolve_tree) {
1927                 PyErr_SetString(PyExc_NotImplementedError,
1928                                                 "resolve_tree not supported with svn < 1.6");
1929                 apr_pool_destroy(temp_pool);
1930                 return NULL;
1931         } else {
1932                 RUN_SVN_WITH_POOL(temp_pool,
1933                           svn_wc_resolved_conflict3(path, admobj->adm, resolve_text, 
1934                                                                                         resolve_props, depth,
1935                                                                                         conflict_choice, py_wc_notify_func,
1936                                                                                    (void *)notify_func, py_cancel_check,
1937                                                                                    NULL, temp_pool));
1938         }
1939 #else
1940         if (resolve_tree) {
1941                 PyErr_SetString(PyExc_NotImplementedError,
1942                                                 "resolve_tree not supported with svn < 1.6");
1943                 apr_pool_destroy(temp_pool);
1944                 return NULL;
1945         } else if (depth != svn_depth_infinity && depth != svn_depth_files) {
1946                 PyErr_SetString(PyExc_NotImplementedError,
1947                                                 "only infinity and files values for depth are supported");
1948                 apr_pool_destroy(temp_pool);
1949                 return NULL;
1950         } else if (conflict_choice != 0) {
1951                 PyErr_SetString(PyExc_NotImplementedError,
1952                                                 "conflict choice not supported with svn < 1.5");
1953                 apr_pool_destroy(temp_pool);
1954                 return NULL;
1955         } else {
1956                 RUN_SVN_WITH_POOL(temp_pool,
1957                           svn_wc_resolved_conflict2(path, admobj->adm, resolve_text,
1958                                                                                         resolve_props,
1959                                                                                         (depth == svn_depth_infinity),
1960                                                                                         py_wc_notify_func,
1961                                                                                    (void *)notify_func, py_cancel_check,
1962                                                                                    NULL,
1963                                                                                    temp_pool));
1964         }
1965 #endif
1966
1967         apr_pool_destroy(temp_pool);
1968
1969         Py_RETURN_NONE;
1970 }
1971
1972 static PyObject *conflicted(PyObject *self, PyObject *args)
1973 {
1974         char *path;
1975         apr_pool_t *temp_pool;
1976         PyObject *ret;
1977         AdmObject *admobj = (AdmObject *)self;
1978         svn_boolean_t text_conflicted, prop_conflicted, tree_conflicted;
1979
1980         if (!PyArg_ParseTuple(args, "s", &path))
1981                 return NULL;
1982
1983         ADM_CHECK_CLOSED(admobj);
1984
1985         temp_pool = Pool(NULL);
1986         if (temp_pool == NULL)
1987                 return NULL;
1988
1989 #if ONLY_SINCE_SVN(1, 6)
1990         RUN_SVN_WITH_POOL(temp_pool, svn_wc_conflicted_p2(&text_conflicted,
1991                 &prop_conflicted, &tree_conflicted, path, admobj->adm, temp_pool));
1992
1993         ret = Py_BuildValue("(bbb)", text_conflicted, prop_conflicted, tree_conflicted);
1994 #else
1995         RUN_SVN_WITH_POOL(temp_pool, svn_wc_conflicted_p(&text_conflicted,
1996                 &prop_conflicted, path, admobj->adm, temp_pool));
1997
1998         ret = Py_BuildValue("(bbO)", text_conflicted, prop_conflicted, Py_None);
1999 #endif
2000
2001         apr_pool_destroy(temp_pool);
2002
2003         return ret;
2004 }
2005
2006 /**
2007  * Determine the status of a file in the specified working copy.
2008  *
2009  * :return: A status object.
2010  */
2011 static PyObject *ra_status(PyObject *self, PyObject *args)
2012 {
2013         char *path;
2014         svn_wc_status2_t *st;
2015         apr_pool_t *temp_pool;
2016         PyObject *ret;
2017         AdmObject *admobj = (AdmObject *)self;
2018
2019         if (!PyArg_ParseTuple(args, "s", &path))
2020                 return NULL;
2021
2022         ADM_CHECK_CLOSED(admobj);
2023
2024         temp_pool = Pool(NULL);
2025         if (temp_pool == NULL)
2026                 return NULL;
2027
2028         RUN_SVN_WITH_POOL(temp_pool, 
2029                         svn_wc_status2(
2030                                 &st,
2031                                 svn_dirent_canonicalize(svn_path_join(svn_wc_adm_access_path(admobj->adm), path, temp_pool), temp_pool),
2032                                 admobj->adm,
2033                                 temp_pool));
2034
2035         ret = py_status(st);
2036
2037         apr_pool_destroy(temp_pool);
2038
2039         return (PyObject*)ret;
2040 }
2041
2042 static PyMethodDef adm_methods[] = { 
2043         { "prop_set", adm_prop_set, METH_VARARGS, "S.prop_set(name, value, path, skip_checks=False)" },
2044         { "access_path", (PyCFunction)adm_access_path, METH_NOARGS, 
2045                 "S.access_path() -> path\n"
2046                 "Returns the base path for this working copy handle." },
2047         { "prop_get", adm_prop_get, METH_VARARGS, "S.prop_get(name, path) -> value" },
2048         { "entries_read", adm_entries_read, METH_VARARGS, "S.entries_read(include_hidden=False) -> dict" },
2049         { "walk_entries", adm_walk_entries, METH_VARARGS, 
2050                 "S.walk_entries(path, callback, show_hidden=False)\n"
2051                 "callback should be a function that takes a path and a wc entry" },
2052         { "locked", (PyCFunction)adm_locked, METH_NOARGS, 
2053                 "S.locked() -> bool" },
2054         { "get_prop_diffs", adm_get_prop_diffs, METH_VARARGS, 
2055                 "S.get_prop_diffs(path) -> (propchanges, originalprops)" },
2056         { "add", (PyCFunction)adm_add, METH_VARARGS|METH_KEYWORDS, "S.add(path, copyfrom_url=None, copyfrom_rev=-1, notify_func=None)" },
2057         { "copy", adm_copy, METH_VARARGS, "S.copy(src_path, dest_path, notify_func=None)" },
2058         { "delete", (PyCFunction)adm_delete, METH_VARARGS|METH_KEYWORDS, "S.delete(path, notify_func=None, keep_local=False)" },
2059         { "crawl_revisions", (PyCFunction)adm_crawl_revisions, METH_VARARGS|METH_KEYWORDS, 
2060                 "S.crawl_revisions(path, reporter, restore_files=True, recurse=True, use_commit_times=True, notify_func=None) -> None" },
2061         { "get_update_editor", adm_get_update_editor, METH_VARARGS, NULL },
2062         { "close", (PyCFunction)adm_close, METH_NOARGS, 
2063                 "S.close()" },
2064         { "entry", (PyCFunction)adm_entry, METH_VARARGS, 
2065                 "s.entry(path, show_hidden=False) -> entry" },
2066         { "process_committed", (PyCFunction)adm_process_committed, METH_VARARGS|METH_KEYWORDS, "S.process_committed(path, recurse, new_revnum, rev_date, rev_author, wcprop_changes=None, remove_lock=False, digest=None)" },
2067         { "process_committed_queue", (PyCFunction)adm_process_committed_queue, METH_VARARGS, "S.process_committed_queue(queue, new_revnum, rev_date, rev_author)" },
2068         { "remove_lock", (PyCFunction)adm_remove_lock, METH_VARARGS, "S.remove_lock(path)" }, 
2069         { "has_binary_prop", (PyCFunction)adm_has_binary_prop, METH_VARARGS, "S.has_binary_prop(path) -> bool" },
2070         { "text_modified", (PyCFunction)adm_text_modified, METH_VARARGS, "S.text_modified(filename, force_comparison=False) -> bool" },
2071         { "props_modified", (PyCFunction)adm_props_modified, METH_VARARGS, "S.props_modified(filename) -> bool" },
2072         { "get_ancestry", (PyCFunction)get_ancestry, METH_VARARGS,
2073                 "S.get_ancestry(path) -> (url, rev)" },
2074         { "maybe_set_repos_root", (PyCFunction)maybe_set_repos_root, METH_VARARGS, "S.maybe_set_repos_root(path, repos)" },
2075         { "add_repos_file", (PyCFunction)add_repos_file, METH_KEYWORDS, 
2076                 "S.add_repos_file(dst_path, new_base_contents, new_contents, new_base_props, new_props, copyfrom_url=None, copyfrom_rev=-1, notify_func=None)" },
2077         { "mark_missing_deleted", (PyCFunction)mark_missing_deleted, METH_VARARGS,
2078                 "S.mark_missing_deleted(path)" },
2079         { "remove_from_revision_control", (PyCFunction)remove_from_revision_control, METH_VARARGS,
2080                 "S.remove_from_revision_control(name, destroy_wf=False, instant_error=False)" },
2081         { "relocate", (PyCFunction)relocate, METH_VARARGS,
2082                 "S.relocate(path, from, to, recurse=TRUE, validator=None)" },
2083         { "crop_tree", (PyCFunction)crop_tree, METH_VARARGS,
2084                 "S.crop_tree(target, depth, notify_func=None, cancel=None)" },
2085         { "translated_stream", (PyCFunction)translated_stream, METH_VARARGS,
2086                 "S.translated_stream(path, versioned_file, flags) -> stream" },
2087         { "is_wc_root", (PyCFunction)is_wc_root, METH_VARARGS,
2088                 "S.is_wc_root(path) -> wc_root" },
2089         { "transmit_text_deltas", (PyCFunction)transmit_text_deltas, METH_VARARGS,
2090                 "S.transmit_text_deltas(fulltext, editor) -> (tempfile, digest)" },
2091         { "transmit_prop_deltas", (PyCFunction)transmit_prop_deltas, METH_VARARGS,
2092                 "S.transmit_prop_deltas(path, entry, editor)" },
2093         { "probe_retrieve", (PyCFunction)probe_retrieve, METH_VARARGS,
2094                 "S.probe_retrieve(path) -> WorkingCopy" },
2095         { "retrieve", (PyCFunction)retrieve, METH_VARARGS,
2096                 "S.retrieve(path) -> WorkingCopy" },
2097         { "probe_try", (PyCFunction)probe_try, METH_VARARGS,
2098                 "S.probe_try(path, write_lock=False, levels_to_lock=-1)" },
2099         { "conflicted", (PyCFunction)conflicted, METH_VARARGS,
2100                 "S.conflicted(path) -> (text_conflicted, prop_conflicted, tree_conflicted)" },
2101         { "resolved_conflict", (PyCFunction)resolved_conflict, METH_VARARGS,
2102                 "S.resolved_conflict(path, resolve_text, resolve_props, resolve_tree, depth, conflict_choice, notify_func=None, cancel=None)" },
2103         { "status", (PyCFunction)ra_status, METH_VARARGS, "status(wc_path) -> Status" },
2104         { NULL, }
2105 };
2106
2107 static PyTypeObject Adm_Type = {
2108         PyObject_HEAD_INIT(NULL) 0,
2109         "wc.WorkingCopy", /*    const char *tp_name;  For printing, in format "<module>.<name>" */
2110         sizeof(AdmObject), 
2111         0,/*    Py_ssize_t tp_basicsize, tp_itemsize;  For allocation */
2112         
2113         /* Methods to implement standard operations */
2114         
2115         adm_dealloc, /* destructor tp_dealloc;  */
2116         NULL, /*        printfunc tp_print;     */
2117         NULL, /*        getattrfunc tp_getattr; */
2118         NULL, /*        setattrfunc tp_setattr; */
2119         NULL, /*        cmpfunc tp_compare;     */
2120         adm_repr, /*    reprfunc tp_repr;       */
2121         
2122         /* Method suites for standard classes */
2123         
2124         NULL, /*        PyNumberMethods *tp_as_number;  */
2125         NULL, /*        PySequenceMethods *tp_as_sequence;      */
2126         NULL, /*        PyMappingMethods *tp_as_mapping;        */
2127         
2128         /* More standard operations (here for binary compatibility) */
2129         
2130         NULL, /*        hashfunc tp_hash;       */
2131         NULL, /*        ternaryfunc tp_call;    */
2132         adm_repr, /*    reprfunc tp_repr;       */
2133         NULL, /*        getattrofunc tp_getattro;       */
2134         NULL, /*        setattrofunc tp_setattro;       */
2135         
2136         /* Functions to access object as input/output buffer */
2137         NULL, /*        PyBufferProcs *tp_as_buffer;    */
2138         
2139         /* Flags to define presence of optional/expanded features */
2140         0, /*   long tp_flags;  */
2141         
2142         "Local working copy", /*        const char *tp_doc;  Documentation string */
2143         
2144         /* Assigned meaning in release 2.0 */
2145         /* call function for all accessible objects */
2146         NULL, /*        traverseproc tp_traverse;       */
2147         
2148         /* delete references to contained objects */
2149         NULL, /*        inquiry tp_clear;       */
2150         
2151         /* Assigned meaning in release 2.1 */
2152         /* rich comparisons */
2153         NULL, /*        richcmpfunc tp_richcompare;     */
2154         
2155         /* weak reference enabler */
2156         0, /*   Py_ssize_t tp_weaklistoffset;   */
2157         
2158         /* Added in release 2.2 */
2159         /* Iterators */
2160         NULL, /*        getiterfunc tp_iter;    */
2161         NULL, /*        iternextfunc tp_iternext;       */
2162         
2163         /* Attribute descriptor and subclassing stuff */
2164         adm_methods, /* struct PyMethodDef *tp_methods; */
2165         NULL, /*        struct PyMemberDef *tp_members; */
2166         NULL, /*        struct PyGetSetDef *tp_getset;  */
2167         NULL, /*        struct _typeobject *tp_base;    */
2168         NULL, /*        PyObject *tp_dict;      */
2169         NULL, /*        descrgetfunc tp_descr_get;      */
2170         NULL, /*        descrsetfunc tp_descr_set;      */
2171         0, /*   Py_ssize_t tp_dictoffset;       */
2172         NULL, /*        initproc tp_init;       */
2173         NULL, /*        allocfunc tp_alloc;     */
2174         adm_init, /*    newfunc tp_new; */
2175 };
2176
2177 static void committed_queue_dealloc(PyObject *self)
2178 {
2179         apr_pool_destroy(((CommittedQueueObject *)self)->pool);
2180         PyObject_Del(self);
2181 }
2182
2183 static PyObject *committed_queue_repr(PyObject *self)
2184 {
2185         CommittedQueueObject *cqobj = (CommittedQueueObject *)self;
2186
2187         return PyString_FromFormat("<wc.CommittedQueue at 0x%p>", cqobj->queue);
2188 }
2189
2190 static PyObject *committed_queue_init(PyTypeObject *self, PyObject *args, PyObject *kwargs)
2191 {
2192         CommittedQueueObject *ret;
2193         char *kwnames[] = { NULL };
2194
2195         if (!PyArg_ParseTupleAndKeywords(args, kwargs, "", kwnames))
2196                 return NULL;
2197
2198         ret = PyObject_New(CommittedQueueObject, &CommittedQueue_Type);
2199         if (ret == NULL)
2200                 return NULL;
2201
2202         ret->pool = Pool(NULL);
2203         if (ret->pool == NULL)
2204                 return NULL;
2205         ret->queue = svn_wc_committed_queue_create(ret->pool);
2206         if (ret->queue == NULL) {
2207                 PyObject_Del(ret);
2208                 PyErr_NoMemory();
2209                 return NULL;
2210         }
2211
2212         return (PyObject *)ret;
2213 }
2214
2215 static PyObject *committed_queue_queue(CommittedQueueObject *self, PyObject *args)
2216 {
2217         char *path;
2218         AdmObject *admobj;
2219         PyObject *py_wcprop_changes = Py_None;
2220         svn_boolean_t remove_lock = FALSE, remove_changelist = FALSE;
2221         char *digest = NULL;
2222         svn_boolean_t recurse = FALSE;
2223         apr_pool_t *temp_pool;
2224         apr_array_header_t *wcprop_changes;
2225         int digest_len;
2226
2227         if (!PyArg_ParseTuple(args, "sO!|bObbz#", &path, &Adm_Type, &admobj,
2228                                                   &recurse, &py_wcprop_changes, &remove_lock,
2229                                                   &remove_changelist, &digest, &digest_len))
2230                 return NULL;
2231
2232         temp_pool = Pool(NULL);
2233         if (temp_pool == NULL)
2234                 return NULL;
2235
2236         if (!py_dict_to_wcprop_changes(py_wcprop_changes, self->pool, &wcprop_changes)) {
2237                 apr_pool_destroy(temp_pool);
2238                 return NULL;
2239         }
2240
2241         path = apr_pstrdup(self->pool, path);
2242         if (path == NULL) {
2243                 PyErr_NoMemory();
2244                 return NULL;
2245         }
2246
2247         if (digest != NULL) {
2248                 if (digest_len != APR_MD5_DIGESTSIZE) {
2249                         PyErr_SetString(PyExc_ValueError, "Invalid size for md5 digest");
2250                         apr_pool_destroy(temp_pool);
2251                         return NULL;
2252                 }
2253                 digest = apr_pstrdup(self->pool, digest);
2254                 if (digest == NULL) {
2255                         PyErr_NoMemory();
2256                         return NULL;
2257                 }
2258         }
2259
2260         RUN_SVN_WITH_POOL(temp_pool, 
2261                 svn_wc_queue_committed(&self->queue, path, admobj->adm, recurse,
2262                                                            wcprop_changes, remove_lock, remove_changelist,
2263                                                            (unsigned char *)digest, temp_pool));
2264
2265         apr_pool_destroy(temp_pool);
2266
2267         Py_RETURN_NONE;
2268 }
2269
2270 static PyMethodDef committed_queue_methods[] = {
2271         { "queue", (PyCFunction)committed_queue_queue, METH_VARARGS,
2272                 "S.queue(path, adm, recurse, wcprop_changes, remove_lock, remove_changelist, digest)" },
2273         { NULL }
2274 };
2275
2276 static PyTypeObject CommittedQueue_Type = {
2277         PyObject_HEAD_INIT(NULL) 0,
2278         "wc.CommittedQueue", /* const char *tp_name;  For printing, in format "<module>.<name>" */
2279         sizeof(CommittedQueueObject), 
2280         0,/*    Py_ssize_t tp_basicsize, tp_itemsize;  For allocation */
2281         
2282         /* Methods to implement standard operations */
2283         
2284         committed_queue_dealloc, /*     destructor tp_dealloc;  */
2285         NULL, /*        printfunc tp_print;     */
2286         NULL, /*        getattrfunc tp_getattr; */
2287         NULL, /*        setattrfunc tp_setattr; */
2288         NULL, /*        cmpfunc tp_compare;     */
2289         committed_queue_repr, /*        reprfunc tp_repr;       */
2290         
2291         /* Method suites for standard classes */
2292         
2293         NULL, /*        PyNumberMethods *tp_as_number;  */
2294         NULL, /*        PySequenceMethods *tp_as_sequence;      */
2295         NULL, /*        PyMappingMethods *tp_as_mapping;        */
2296         
2297         /* More standard operations (here for binary compatibility) */
2298         
2299         NULL, /*        hashfunc tp_hash;       */
2300         NULL, /*        ternaryfunc tp_call;    */
2301         NULL, /*        reprfunc tp_str;        */
2302         NULL, /*        getattrofunc tp_getattro;       */
2303         NULL, /*        setattrofunc tp_setattro;       */
2304         
2305         /* Functions to access object as input/output buffer */
2306         NULL, /*        PyBufferProcs *tp_as_buffer;    */
2307         
2308         /* Flags to define presence of optional/expanded features */
2309         0, /*   long tp_flags;  */
2310         
2311         "Committed queue", /*   const char *tp_doc;  Documentation string */
2312         
2313         /* Assigned meaning in release 2.0 */
2314         /* call function for all accessible objects */
2315         NULL, /*        traverseproc tp_traverse;       */
2316         
2317         /* delete references to contained objects */
2318         NULL, /*        inquiry tp_clear;       */
2319         
2320         /* Assigned meaning in release 2.1 */
2321         /* rich comparisons */
2322         NULL, /*        richcmpfunc tp_richcompare;     */
2323         
2324         /* weak reference enabler */
2325         0, /*   Py_ssize_t tp_weaklistoffset;   */
2326         
2327         /* Added in release 2.2 */
2328         /* Iterators */
2329         NULL, /*        getiterfunc tp_iter;    */
2330         NULL, /*        iternextfunc tp_iternext;       */
2331         
2332         /* Attribute descriptor and subclassing stuff */
2333         committed_queue_methods, /*     struct PyMethodDef *tp_methods; */
2334         NULL, /*        struct PyMemberDef *tp_members; */
2335         NULL, /*        struct PyGetSetDef *tp_getset;  */
2336         NULL, /*        struct _typeobject *tp_base;    */
2337         NULL, /*        PyObject *tp_dict;      */
2338         NULL, /*        descrgetfunc tp_descr_get;      */
2339         NULL, /*        descrsetfunc tp_descr_set;      */
2340         0, /*   Py_ssize_t tp_dictoffset;       */
2341         NULL, /*        initproc tp_init;       */
2342         NULL, /*        allocfunc tp_alloc;     */
2343         committed_queue_init, /*        newfunc tp_new; */
2344 };
2345
2346 /** 
2347  * Determine the revision status of a specified working copy.
2348  *
2349  * :return: Tuple with minimum and maximum revnums found, whether the 
2350  * working copy was switched and whether it was modified.
2351  */
2352 static PyObject *revision_status(PyObject *self, PyObject *args, PyObject *kwargs)
2353 {
2354         char *kwnames[] = { "wc_path", "trail_url", "committed",  NULL };
2355         char *wc_path, *trail_url=NULL;
2356         bool committed=false;
2357         PyObject *ret;
2358          svn_wc_revision_status_t *revstatus;
2359         apr_pool_t *temp_pool;
2360
2361         if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|zb", kwnames, &wc_path,
2362                                                                          &trail_url, &committed))
2363                 return NULL;
2364
2365         temp_pool = Pool(NULL);
2366         if (temp_pool == NULL)
2367                 return NULL;
2368         RUN_SVN_WITH_POOL(temp_pool, 
2369                         svn_wc_revision_status(
2370                                 &revstatus, 
2371                                 svn_dirent_canonicalize(wc_path, temp_pool),
2372                                 trail_url,
2373                                  committed, py_cancel_check, NULL, temp_pool));
2374         ret = Py_BuildValue("(llbb)", revstatus->min_rev, revstatus->max_rev, 
2375                         revstatus->switched, revstatus->modified);
2376         apr_pool_destroy(temp_pool);
2377         return ret;
2378 }
2379
2380 static PyObject *is_normal_prop(PyObject *self, PyObject *args)
2381 {
2382         char *name;
2383
2384         if (!PyArg_ParseTuple(args, "s", &name))
2385                 return NULL;
2386
2387         return PyBool_FromLong(svn_wc_is_normal_prop(name));
2388 }
2389
2390 static PyObject *is_adm_dir(PyObject *self, PyObject *args)
2391 {
2392         char *name;
2393         apr_pool_t *pool;
2394         svn_boolean_t ret;
2395
2396         if (!PyArg_ParseTuple(args, "s", &name))
2397                 return NULL;
2398
2399         pool = Pool(NULL);
2400         if (pool == NULL)
2401                 return NULL;
2402
2403         ret = svn_wc_is_adm_dir(name, pool);
2404
2405         apr_pool_destroy(pool);
2406
2407         return PyBool_FromLong(ret);
2408 }
2409
2410 static PyObject *is_wc_prop(PyObject *self, PyObject *args)
2411 {
2412         char *name;
2413
2414         if (!PyArg_ParseTuple(args, "s", &name))
2415                 return NULL;
2416
2417         return PyBool_FromLong(svn_wc_is_wc_prop(name));
2418 }
2419
2420 static PyObject *is_entry_prop(PyObject *self, PyObject *args)
2421 {
2422         char *name;
2423
2424         if (!PyArg_ParseTuple(args, "s", &name))
2425                 return NULL;
2426
2427         return PyBool_FromLong(svn_wc_is_entry_prop(name));
2428 }
2429
2430 static PyObject *get_adm_dir(PyObject *self)
2431 {
2432         apr_pool_t *pool;
2433         PyObject *ret;
2434         const char *dir;
2435         pool = Pool(NULL);
2436         if (pool == NULL)
2437                 return NULL;
2438         dir = svn_wc_get_adm_dir(pool);
2439         ret = PyString_FromString(dir);
2440         apr_pool_destroy(pool);
2441         return ret;
2442 }
2443
2444 static PyObject *set_adm_dir(PyObject *self, PyObject *args)
2445 {
2446         apr_pool_t *temp_pool;
2447         char *name;
2448
2449         if (!PyArg_ParseTuple(args, "s", &name))
2450                 return NULL;
2451
2452         temp_pool = Pool(NULL);
2453         if (temp_pool == NULL)
2454                 return NULL;
2455         RUN_SVN_WITH_POOL(temp_pool, svn_wc_set_adm_dir(name, temp_pool));
2456         apr_pool_destroy(temp_pool);
2457         Py_RETURN_NONE;
2458 }
2459
2460 static PyObject *get_pristine_copy_path(PyObject *self, PyObject *args)
2461 {
2462         apr_pool_t *pool;
2463         const char *pristine_path;
2464         char *path;
2465         PyObject *ret;
2466
2467         if (!PyArg_ParseTuple(args, "s", &path))
2468                 return NULL;
2469
2470         pool = Pool(NULL);
2471         if (pool == NULL)
2472                 return NULL;
2473         PyErr_WarnEx(PyExc_DeprecationWarning, "get_pristine_copy_path is deprecated. Use get_pristine_contents instead.", 2);
2474         RUN_SVN_WITH_POOL(pool,
2475                   svn_wc_get_pristine_copy_path(svn_dirent_canonicalize(path, pool),
2476                                                                                 &pristine_path, pool));
2477         ret = PyString_FromString(pristine_path);
2478         apr_pool_destroy(pool);
2479         return ret;
2480 }
2481
2482 static PyObject *get_pristine_contents(PyObject *self, PyObject *args)
2483 {
2484         char *path;
2485         apr_pool_t *temp_pool;
2486 #if ONLY_SINCE_SVN(1, 6)
2487         apr_pool_t *stream_pool;
2488         StreamObject *ret;
2489         svn_stream_t *stream;
2490 #else
2491         PyObject *ret;
2492         const char *pristine_path;
2493 #endif
2494
2495         if (!PyArg_ParseTuple(args, "s", &path))
2496                 return NULL;
2497
2498 #if ONLY_SINCE_SVN(1, 6)
2499         stream_pool = Pool(NULL);
2500         if (stream_pool == NULL)
2501                 return NULL;
2502
2503         temp_pool = Pool(stream_pool);
2504         if (temp_pool == NULL) {
2505                 apr_pool_destroy(stream_pool);
2506                 return NULL;
2507         }
2508
2509         RUN_SVN_WITH_POOL(stream_pool, svn_wc_get_pristine_contents(&stream, svn_dirent_canonicalize(path, temp_pool), stream_pool, temp_pool));
2510         apr_pool_destroy(temp_pool);
2511
2512         if (stream == NULL) {
2513                 apr_pool_destroy(stream_pool);
2514                 Py_RETURN_NONE;
2515         }
2516
2517         ret = PyObject_New(StreamObject, &Stream_Type);
2518         if (ret == NULL)
2519                 return NULL;
2520
2521         ret->pool = stream_pool;
2522         ret->closed = FALSE;
2523         ret->stream = stream;
2524
2525         return (PyObject *)ret;
2526 #else
2527         temp_pool = Pool(NULL);
2528         if (temp_pool == NULL)
2529                 return NULL;
2530         RUN_SVN_WITH_POOL(temp_pool, svn_wc_get_pristine_copy_path(svn_dirent_canonicalize(path, temp_pool), &pristine_path, temp_pool));
2531         ret = PyFile_FromString((char *)pristine_path, "rb");
2532         apr_pool_destroy(temp_pool);
2533         return ret;
2534 #endif
2535 }
2536
2537 static PyObject *ensure_adm(PyObject *self, PyObject *args, PyObject *kwargs)
2538 {
2539         char *path, *uuid, *url;
2540         char *repos=NULL; 
2541         svn_revnum_t rev=-1;
2542         apr_pool_t *pool;
2543         char *kwnames[] = { "path", "uuid", "url", "repos", "rev", "depth", NULL };
2544         svn_depth_t depth = svn_depth_infinity;
2545
2546         if (!PyArg_ParseTupleAndKeywords(args, kwargs, "sss|sli", kwnames, 
2547                                                                          &path, &uuid, &url, &repos, &rev, &depth))
2548                 return NULL;
2549
2550         pool = Pool(NULL);
2551         if (pool == NULL)
2552                 return NULL;
2553 #if ONLY_SINCE_SVN(1, 5)
2554         RUN_SVN_WITH_POOL(pool, 
2555                                           svn_wc_ensure_adm3(svn_dirent_canonicalize(path, pool),
2556                                                                                  uuid, url, repos, rev, depth, pool));
2557 #else
2558         if (depth != svn_depth_infinity) {
2559                 PyErr_SetString(PyExc_NotImplementedError, 
2560                                                 "depth != infinity not supported with svn < 1.5");
2561                 apr_pool_destroy(pool);
2562                 return NULL;
2563         }
2564         RUN_SVN_WITH_POOL(pool, 
2565                                           svn_wc_ensure_adm2(svn_dirent_canonicalize(path, pool),
2566                                                                                  uuid, url, repos, rev, pool));
2567 #endif
2568         apr_pool_destroy(pool);
2569         Py_RETURN_NONE;
2570 }
2571
2572 static PyObject *check_wc(PyObject *self, PyObject *args)
2573 {
2574         char *path;
2575         apr_pool_t *pool;
2576         int wc_format;
2577
2578         if (!PyArg_ParseTuple(args, "s", &path))
2579                 return NULL;
2580
2581         pool = Pool(NULL);
2582         if (pool == NULL)
2583                 return NULL;
2584         RUN_SVN_WITH_POOL(pool, svn_wc_check_wc(svn_dirent_canonicalize(path, pool), &wc_format, pool));
2585         apr_pool_destroy(pool);
2586         return PyLong_FromLong(wc_format);
2587 }
2588
2589 static PyObject *cleanup_wc(PyObject *self, PyObject *args, PyObject *kwargs)
2590 {
2591         char *path;
2592         char *diff3_cmd = NULL;
2593         char *kwnames[] = { "path", "diff3_cmd", NULL };
2594         apr_pool_t *temp_pool;
2595
2596         if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|z", kwnames, 
2597                                                                          &path, &diff3_cmd)) 
2598                 return NULL;
2599
2600         temp_pool = Pool(NULL);
2601         if (temp_pool == NULL)
2602                 return NULL;
2603         RUN_SVN_WITH_POOL(temp_pool, 
2604                                 svn_wc_cleanup2(path, diff3_cmd, py_cancel_check, NULL,
2605                                                                 temp_pool));
2606         apr_pool_destroy(temp_pool);
2607
2608         Py_RETURN_NONE;
2609 }
2610
2611 static PyObject *match_ignore_list(PyObject *self, PyObject *args)
2612 {
2613 #if ONLY_SINCE_SVN(1, 5)
2614         char *str;
2615         PyObject *py_list;
2616         apr_array_header_t *list;
2617         apr_pool_t *temp_pool;
2618         svn_boolean_t ret;
2619
2620         if (!PyArg_ParseTuple(args, "sO", &str, &py_list))
2621                 return NULL;
2622
2623         temp_pool = Pool(NULL);
2624
2625         if (!string_list_to_apr_array(temp_pool, py_list, &list)) {
2626                 apr_pool_destroy(temp_pool);
2627                 return NULL;
2628         }
2629
2630         ret = svn_wc_match_ignore_list(str, list, temp_pool);
2631
2632         apr_pool_destroy(temp_pool);
2633
2634         return PyBool_FromLong(ret);
2635 #else
2636         PyErr_SetNone(PyExc_NotImplementedError);
2637         return NULL;
2638 #endif
2639 }
2640
2641 static PyMethodDef wc_methods[] = {
2642         { "check_wc", check_wc, METH_VARARGS, "check_wc(path) -> version\n"
2643                 "Check whether path contains a Subversion working copy\n"
2644                 "return the workdir version"},
2645         { "cleanup", (PyCFunction)cleanup_wc, METH_VARARGS|METH_KEYWORDS, "cleanup(path, diff3_cmd=None)\n" },
2646         { "ensure_adm", (PyCFunction)ensure_adm, METH_KEYWORDS|METH_VARARGS, 
2647                 "ensure_adm(path, uuid, url, repos=None, rev=None)" },
2648         { "get_adm_dir", (PyCFunction)get_adm_dir, METH_NOARGS, 
2649                 "get_adm_dir() -> name" },
2650         { "set_adm_dir", (PyCFunction)set_adm_dir, METH_VARARGS,
2651                 "set_adm_dir(name)" },
2652         { "get_pristine_copy_path", get_pristine_copy_path, METH_VARARGS, 
2653                 "get_pristine_copy_path(path) -> path" },
2654         { "get_pristine_contents", get_pristine_contents, METH_VARARGS,
2655                 "get_pristine_contents(path) -> stream" },
2656         { "is_adm_dir", is_adm_dir, METH_VARARGS, 
2657                 "is_adm_dir(name) -> bool" },
2658         { "is_normal_prop", is_normal_prop, METH_VARARGS, 
2659                 "is_normal_prop(name) -> bool" },
2660         { "is_entry_prop", is_entry_prop, METH_VARARGS, 
2661                 "is_entry_prop(name) -> bool" },
2662         { "is_wc_prop", is_wc_prop, METH_VARARGS, 
2663                 "is_wc_prop(name) -> bool" },
2664         { "revision_status", (PyCFunction)revision_status, METH_KEYWORDS|METH_VARARGS, "revision_status(wc_path, trail_url=None, committed=False) -> (min_rev, max_rev, switched, modified)" },
2665         { "version", (PyCFunction)version, METH_NOARGS,
2666                 "version() -> (major, minor, patch, tag)\n\n"
2667                 "Version of libsvn_wc currently used."
2668         },
2669         { "api_version", (PyCFunction)api_version, METH_NOARGS,
2670                 "api_version() -> (major, minor, patch, tag)\n\n"
2671                 "Version of libsvn_wc Subvertpy was compiled against."
2672         },
2673         { "match_ignore_list", (PyCFunction)match_ignore_list, METH_VARARGS,
2674                 "match_ignore_list(str, patterns) -> bool" },
2675         { "get_actual_target", (PyCFunction)get_actual_target, METH_VARARGS,
2676                 "get_actual_target(path) -> (anchor, target)" },
2677         { NULL, }
2678 };
2679
2680 static PyObject *
2681 moduleinit(void)
2682 {
2683         PyObject *mod;
2684
2685         if (PyType_Ready(&Entry_Type) < 0)
2686                 return NULL;
2687
2688         if (PyType_Ready(&Status_Type) < 0)
2689                 return NULL;
2690
2691         if (PyType_Ready(&Adm_Type) < 0)
2692                 return NULL;
2693
2694         if (PyType_Ready(&Editor_Type) < 0)
2695                 return NULL;
2696
2697         if (PyType_Ready(&FileEditor_Type) < 0)
2698                 return NULL;
2699
2700         if (PyType_Ready(&DirectoryEditor_Type) < 0)
2701                 return NULL;
2702
2703         if (PyType_Ready(&TxDeltaWindowHandler_Type) < 0)
2704                 return NULL;
2705
2706         if (PyType_Ready(&Stream_Type) < 0)
2707                 return NULL;
2708
2709         if (PyType_Ready(&CommittedQueue_Type) < 0)
2710                 return NULL;
2711
2712         apr_initialize();
2713
2714 #if PY_MAJOR_VERSION >= 3
2715         static struct PyModuleDef moduledef = {
2716           PyModuleDef_HEAD_INIT,
2717           "wc",         /* m_name */
2718           "Working Copies", /* m_doc */
2719           -1,              /* m_size */
2720           wc_methods, /* m_methods */
2721           NULL,            /* m_reload */
2722           NULL,            /* m_traverse */
2723           NULL,            /* m_clear*/
2724           NULL,            /* m_free */
2725         };
2726         mod = PyModule_Create(&moduledef);
2727 #else
2728         mod = Py_InitModule3("wc", wc_methods, "Working Copies");
2729 #endif
2730         if (mod == NULL)
2731                 return NULL;
2732
2733         PyModule_AddIntConstant(mod, "SCHEDULE_NORMAL", 0);
2734         PyModule_AddIntConstant(mod, "SCHEDULE_ADD", 1);
2735         PyModule_AddIntConstant(mod, "SCHEDULE_DELETE", 2);
2736         PyModule_AddIntConstant(mod, "SCHEDULE_REPLACE", 3);
2737
2738 #if ONLY_SINCE_SVN(1, 5)
2739         PyModule_AddIntConstant(mod, "CONFLICT_CHOOSE_POSTPONE",
2740                                                         svn_wc_conflict_choose_postpone);
2741         PyModule_AddIntConstant(mod, "CONFLICT_CHOOSE_BASE",
2742                                                         svn_wc_conflict_choose_base);
2743         PyModule_AddIntConstant(mod, "CONFLICT_CHOOSE_THEIRS_FULL",
2744                                                         svn_wc_conflict_choose_theirs_full);
2745         PyModule_AddIntConstant(mod, "CONFLICT_CHOOSE_MINE_FULL",
2746                                                         svn_wc_conflict_choose_mine_full);
2747         PyModule_AddIntConstant(mod, "CONFLICT_CHOOSE_THEIRS_CONFLICT",
2748                                                         svn_wc_conflict_choose_theirs_conflict);
2749         PyModule_AddIntConstant(mod, "CONFLICT_CHOOSE_MINE_CONFLICT",
2750                                                         svn_wc_conflict_choose_mine_conflict);
2751         PyModule_AddIntConstant(mod, "CONFLICT_CHOOSE_MERGED",
2752                                                         svn_wc_conflict_choose_merged);
2753 #endif
2754
2755         PyModule_AddIntConstant(mod, "STATUS_NONE", svn_wc_status_none);
2756         PyModule_AddIntConstant(mod, "STATUS_UNVERSIONED", svn_wc_status_unversioned);
2757         PyModule_AddIntConstant(mod, "STATUS_NORMAL", svn_wc_status_normal);
2758         PyModule_AddIntConstant(mod, "STATUS_ADDED", svn_wc_status_added);
2759         PyModule_AddIntConstant(mod, "STATUS_MISSING", svn_wc_status_missing);
2760         PyModule_AddIntConstant(mod, "STATUS_DELETED", svn_wc_status_deleted);
2761         PyModule_AddIntConstant(mod, "STATUS_REPLACED", svn_wc_status_replaced);
2762         PyModule_AddIntConstant(mod, "STATUS_MODIFIED", svn_wc_status_modified);
2763         PyModule_AddIntConstant(mod, "STATUS_MERGED", svn_wc_status_merged);
2764         PyModule_AddIntConstant(mod, "STATUS_CONFLICTED", svn_wc_status_conflicted);
2765         PyModule_AddIntConstant(mod, "STATUS_IGNORED", svn_wc_status_ignored);
2766         PyModule_AddIntConstant(mod, "STATUS_OBSTRUCTED", svn_wc_status_obstructed);
2767         PyModule_AddIntConstant(mod, "STATUS_EXTERNAL", svn_wc_status_external);
2768         PyModule_AddIntConstant(mod, "STATUS_INCOMPLETE", svn_wc_status_incomplete);
2769
2770         PyModule_AddIntConstant(mod, "TRANSLATE_FROM_NF", SVN_WC_TRANSLATE_FROM_NF);
2771         PyModule_AddIntConstant(mod, "TRANSLATE_TO_NF", SVN_WC_TRANSLATE_TO_NF);
2772         PyModule_AddIntConstant(mod, "TRANSLATE_FORCE_EOL_REPAIR", SVN_WC_TRANSLATE_FORCE_EOL_REPAIR);
2773         PyModule_AddIntConstant(mod, "TRANSLATE_NO_OUTPUT_CLEANUP", SVN_WC_TRANSLATE_NO_OUTPUT_CLEANUP);
2774         PyModule_AddIntConstant(mod, "TRANSLATE_FORCE_COPY", SVN_WC_TRANSLATE_FORCE_COPY);
2775         PyModule_AddIntConstant(mod, "TRANSLATE_USE_GLOBAL_TMP", SVN_WC_TRANSLATE_USE_GLOBAL_TMP);
2776
2777 #if ONLY_SINCE_SVN(1, 5)
2778         PyModule_AddIntConstant(mod, "CONFLICT_CHOOSE_POSTPONE", svn_wc_conflict_choose_postpone);
2779         PyModule_AddIntConstant(mod, "CONFLICT_CHOOSE_BASE", svn_wc_conflict_choose_base);
2780         PyModule_AddIntConstant(mod, "CONFLICT_CHOOSE_THEIRS_FULL", svn_wc_conflict_choose_theirs_full);
2781         PyModule_AddIntConstant(mod, "CONFLICT_CHOOSE_MINE_FULL", svn_wc_conflict_choose_mine_full);
2782         PyModule_AddIntConstant(mod, "CONFLICT_CHOOSE_THEIRS_CONFLICT", svn_wc_conflict_choose_theirs_conflict);
2783         PyModule_AddIntConstant(mod, "CONFLICT_CHOOSE_MINE_CONFLICT", svn_wc_conflict_choose_mine_conflict);
2784         PyModule_AddIntConstant(mod, "CONFLICT_CHOOSE_MERGED", svn_wc_conflict_choose_merged);
2785 #endif
2786
2787 #if ONLY_BEFORE_SVN(1, 7)
2788         /* Subversion 1.7 has a couple of significant behaviour changes that break subvertpy.
2789          * We haven't updated the code to deal with these changes in behaviour yet.
2790          * */
2791         PyModule_AddObject(mod, "WorkingCopy", (PyObject *)&Adm_Type);
2792         Py_INCREF(&Adm_Type);
2793
2794         PyModule_AddObject(mod, "CommittedQueue", (PyObject *)&CommittedQueue_Type);
2795         Py_INCREF(&CommittedQueue_Type);
2796 #endif
2797
2798         return mod;
2799 }
2800
2801 #if PY_MAJOR_VERSION >= 3
2802 PyMODINIT_FUNC
2803 PyInit_wc(void)
2804 {
2805         return moduleinit();
2806 }
2807 #else
2808 PyMODINIT_FUNC
2809 initwc(void)
2810 {
2811         moduleinit();
2812 }
2813 #endif