Consistently use absolute paths.
[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 #include <apr_sha1.h>
28 #include <fcntl.h>
29
30 #include "util.h"
31 #include "editor.h"
32 #include "wc.h"
33
34 #ifndef T_BOOL
35 #define T_BOOL T_BYTE
36 #endif
37
38 typedef struct {
39     PyObject_HEAD
40     svn_lock_t *lock;
41     apr_pool_t *pool;
42 } LockObject;
43 extern PyTypeObject Lock_Type;
44
45 #if ONLY_BEFORE_SVN(1, 5)
46 struct svn_wc_committed_queue_t
47 {
48     apr_pool_t *pool;
49     apr_array_header_t *queue;
50     svn_boolean_t have_recursive;
51 };
52
53 typedef struct
54 {
55     const char *path;
56     svn_wc_adm_access_t *adm_access;
57     svn_boolean_t recurse;
58     svn_boolean_t remove_lock;
59     apr_array_header_t *wcprop_changes;
60     unsigned char *digest;
61 } committed_queue_item_t;
62
63 svn_wc_committed_queue_t *svn_wc_committed_queue_create(apr_pool_t *pool)
64 {
65     svn_wc_committed_queue_t *q;
66
67     q = apr_palloc(pool, sizeof(*q));
68     q->pool = pool;
69     q->queue = apr_array_make(pool, 1, sizeof(committed_queue_item_t *));
70     q->have_recursive = FALSE;
71
72     return q;
73 }
74
75 svn_error_t *svn_wc_queue_committed(svn_wc_committed_queue_t **queue,
76                         const char *path,
77                         svn_wc_adm_access_t *adm_access,
78                         svn_boolean_t recurse,
79                         apr_array_header_t *wcprop_changes,
80                         svn_boolean_t remove_lock,
81                         svn_boolean_t remove_changelist,
82                         const unsigned char *digest,
83                         apr_pool_t *scratch_pool)
84 {
85   committed_queue_item_t *cqi;
86
87   (*queue)->have_recursive |= recurse;
88
89   /* Use the same pool as the one QUEUE was allocated in,
90      to prevent lifetime issues.  Intermediate operations
91      should use SCRATCH_POOL. */
92
93   /* Add to the array with paths and options */
94   cqi = apr_palloc((*queue)->pool, sizeof(*cqi));
95   cqi->path = path;
96   cqi->adm_access = adm_access;
97   cqi->recurse = recurse;
98   cqi->remove_lock = remove_lock;
99   cqi->wcprop_changes = wcprop_changes;
100   cqi->digest = digest;
101
102   APR_ARRAY_PUSH((*queue)->queue, committed_queue_item_t *) = cqi;
103
104   return SVN_NO_ERROR;
105 }
106
107 #endif
108
109 typedef struct {
110     PyObject_VAR_HEAD
111     apr_pool_t *pool;
112     svn_wc_committed_queue_t *queue;
113 } CommittedQueueObject;
114
115 svn_wc_committed_queue_t *PyObject_GetCommittedQueue(PyObject *obj)
116 {
117     return ((CommittedQueueObject *)obj)->queue;
118 }
119
120 #if ONLY_SINCE_SVN(1, 5)
121 static svn_error_t *py_ra_report3_set_path(void *baton, const char *path,
122                                            svn_revnum_t revision,
123                                            svn_depth_t depth, int start_empty,
124                                            const char *lock_token, apr_pool_t *pool)
125 {
126     PyObject *self = (PyObject *)baton, *py_lock_token, *ret;
127     PyGILState_STATE state = PyGILState_Ensure();
128     if (lock_token == NULL) {
129         py_lock_token = Py_None;
130         Py_INCREF(py_lock_token);
131     } else {
132         py_lock_token = PyBytes_FromString(lock_token);
133     }
134     ret = PyObject_CallMethod(self, "set_path", "slbOi", path, revision,
135                               start_empty, py_lock_token, depth);
136     Py_DECREF(py_lock_token);
137     CB_CHECK_PYRETVAL(ret);
138     Py_DECREF(ret);
139     PyGILState_Release(state);
140     return NULL;
141 }
142
143 static svn_error_t *py_ra_report3_link_path(void *report_baton,
144                                             const char *path, const char *url,
145                                             svn_revnum_t revision,
146                                             svn_depth_t depth, int start_empty,
147                                             const char *lock_token, apr_pool_t *pool)
148 {
149     PyObject *self = (PyObject *)report_baton, *ret, *py_lock_token;
150     PyGILState_STATE state = PyGILState_Ensure();
151     if (lock_token == NULL) {
152         py_lock_token = Py_None;
153         Py_INCREF(py_lock_token);
154     } else {
155         py_lock_token = PyBytes_FromString(lock_token);
156     }
157     ret = PyObject_CallMethod(self, "link_path", "sslbOi", path, url, revision,
158                               start_empty, py_lock_token, depth);
159     Py_DECREF(py_lock_token);
160     CB_CHECK_PYRETVAL(ret);
161     Py_DECREF(ret);
162     PyGILState_Release(state);
163     return NULL;
164 }
165
166 #endif
167
168 static svn_error_t *py_ra_report2_set_path(void *baton, const char *path,
169                                            svn_revnum_t revision,
170                                            int start_empty, const char *lock_token,
171                                            apr_pool_t *pool)
172 {
173     PyObject *self = (PyObject *)baton, *py_lock_token, *ret;
174     PyGILState_STATE state = PyGILState_Ensure();
175     if (lock_token == NULL) {
176         py_lock_token = Py_None;
177         Py_INCREF(py_lock_token);
178     } else {
179         py_lock_token = PyBytes_FromString(lock_token);
180     }
181     ret = PyObject_CallMethod(self, "set_path", "slbOi", path, revision,
182                               start_empty, py_lock_token, svn_depth_infinity);
183     CB_CHECK_PYRETVAL(ret);
184     Py_DECREF(ret);
185     PyGILState_Release(state);
186     return NULL;
187 }
188
189 static svn_error_t *py_ra_report2_link_path(void *report_baton,
190                                             const char *path, const char *url,
191                                             svn_revnum_t revision,
192                                             int start_empty,
193                                             const char *lock_token,
194                                             apr_pool_t *pool)
195 {
196     PyObject *self = (PyObject *)report_baton, *ret, *py_lock_token;
197     PyGILState_STATE state = PyGILState_Ensure();
198     if (lock_token == NULL) {
199         py_lock_token = Py_None;
200         Py_INCREF(py_lock_token);
201     } else {
202         py_lock_token = PyBytes_FromString(lock_token);
203     }
204     ret = PyObject_CallMethod(self, "link_path", "sslbOi", path, url, revision,
205                               start_empty, py_lock_token, svn_depth_infinity);
206     CB_CHECK_PYRETVAL(ret);
207     Py_DECREF(ret);
208     PyGILState_Release(state);
209     return NULL;
210 }
211
212 static svn_error_t *py_ra_report_delete_path(void *baton, const char *path,
213                                              apr_pool_t *pool)
214 {
215     PyObject *self = (PyObject *)baton, *ret;
216     PyGILState_STATE state = PyGILState_Ensure();
217     ret = PyObject_CallMethod(self, "delete_path", "s", path);
218     CB_CHECK_PYRETVAL(ret);
219     Py_DECREF(ret);
220     PyGILState_Release(state);
221     return NULL;
222 }
223
224 static svn_error_t *py_ra_report_finish(void *baton, apr_pool_t *pool)
225 {
226     PyObject *self = (PyObject *)baton, *ret;
227     PyGILState_STATE state = PyGILState_Ensure();
228     ret = PyObject_CallMethod(self, "finish", "");
229     CB_CHECK_PYRETVAL(ret);
230     Py_DECREF(ret);
231     PyGILState_Release(state);
232     return NULL;
233 }
234
235 static svn_error_t *py_ra_report_abort(void *baton, apr_pool_t *pool)
236 {
237     PyObject *self = (PyObject *)baton, *ret;
238     PyGILState_STATE state = PyGILState_Ensure();
239     ret = PyObject_CallMethod(self, "abort", "");
240     CB_CHECK_PYRETVAL(ret);
241     Py_DECREF(ret);
242     PyGILState_Release(state);
243     return NULL;
244 }
245
246 #if ONLY_SINCE_SVN(1, 5)
247 const svn_ra_reporter3_t py_ra_reporter3 = {
248     py_ra_report3_set_path,
249     py_ra_report_delete_path,
250     py_ra_report3_link_path,
251     py_ra_report_finish,
252     py_ra_report_abort,
253 };
254 #endif
255
256 const svn_ra_reporter2_t py_ra_reporter2 = {
257     py_ra_report2_set_path,
258     py_ra_report_delete_path,
259     py_ra_report2_link_path,
260     py_ra_report_finish,
261     py_ra_report_abort,
262 };
263
264
265 /**
266  * Get runtime libsvn_wc version information.
267  *
268  * :return: tuple with major, minor, patch version number and tag.
269  */
270 static PyObject *version(PyObject *self)
271 {
272         const svn_version_t *ver = svn_wc_version();
273         return Py_BuildValue("(iiis)", ver->major, ver->minor,
274                                                  ver->patch, ver->tag);
275 }
276
277 SVN_VERSION_DEFINE(svn_api_version);
278
279 /**
280  * Get compile-time libsvn_wc version information.
281  *
282  * :return: tuple with major, minor, patch version number and tag.
283  */
284 static PyObject *api_version(PyObject *self)
285 {
286         const svn_version_t *ver = &svn_api_version;
287         return Py_BuildValue("(iiis)", ver->major, ver->minor,
288                                                  ver->patch, ver->tag);
289 }
290
291
292 void py_wc_notify_func(void *baton, const svn_wc_notify_t *notify, apr_pool_t *pool)
293 {
294         PyObject *func = baton, *ret;
295         if (func == Py_None)
296                 return;
297
298         if (notify->err != NULL) {
299         PyGILState_STATE state = PyGILState_Ensure();
300                 PyObject *excval = PyErr_NewSubversionException(notify->err);
301                 ret = PyObject_CallFunction(func, "O", excval);
302                 Py_DECREF(excval);
303                 Py_XDECREF(ret);
304                 /* If ret was NULL, the cancel func should abort the operation. */
305         PyGILState_Release(state);
306         }
307 }
308 bool py_dict_to_wcprop_changes(PyObject *dict, apr_pool_t *pool, apr_array_header_t **ret)
309 {
310         PyObject *key, *val;
311         Py_ssize_t idx;
312
313         if (dict == Py_None) {
314                 *ret = NULL;
315                 return true;
316         }
317
318         if (!PyDict_Check(dict)) {
319                 PyErr_SetString(PyExc_TypeError, "Expected dictionary with property changes");
320                 return false;
321         }
322
323         *ret = apr_array_make(pool, PyDict_Size(dict), sizeof(char *));
324
325         while (PyDict_Next(dict, &idx, &key, &val)) {
326                 svn_prop_t *prop = apr_palloc(pool, sizeof(svn_prop_t));
327                 prop->name = py_object_to_svn_string(key, pool);
328                 if (prop->name == NULL) {
329                         return false;
330                 }
331                 if (val == Py_None) {
332                         prop->value = NULL;
333                 } else {
334                         if (!PyBytes_Check(val)) {
335                                 PyErr_SetString(PyExc_TypeError, "property values should be bytes");
336                                 return false;
337                         }
338                         prop->value = svn_string_ncreate(PyBytes_AsString(val), PyBytes_Size(val), pool);
339                 }
340                 APR_ARRAY_PUSH(*ret, svn_prop_t *) = prop;
341         }
342
343         return true;
344 }
345
346 #if ONLY_SINCE_SVN(1, 6)
347 svn_error_t *wc_validator3(void *baton, const char *uuid, const char *url, const char *root_url, apr_pool_t *pool)
348 {
349         PyObject *py_validator = baton, *ret;
350     PyGILState_STATE state;
351
352         if (py_validator == Py_None) {
353                 return NULL;
354         }
355     state = PyGILState_Ensure();
356         ret = PyObject_CallFunction(py_validator, "sss", uuid, url, root_url);
357         if (ret == NULL) {
358         PyGILState_Release(state);
359                 return py_svn_error();
360         }
361
362         Py_DECREF(ret);
363
364     PyGILState_Release(state);
365         return NULL;
366 }
367
368 #endif
369
370 svn_error_t *wc_validator2(void *baton, const char *uuid, const char *url, svn_boolean_t root, apr_pool_t *pool)
371 {
372         PyObject *py_validator = baton, *ret;
373     PyGILState_STATE state;
374
375         if (py_validator == Py_None) {
376                 return NULL;
377         }
378
379     state = PyGILState_Ensure();
380         ret = PyObject_CallFunction(py_validator, "ssO", uuid, url, Py_None);
381         if (ret == NULL) {
382         PyGILState_Release(state);
383                 return py_svn_error();
384         }
385
386         Py_DECREF(ret);
387     PyGILState_Release(state);
388
389         return NULL;
390 }
391
392 static PyObject *get_actual_target(PyObject *self, PyObject *args)
393 {
394         const char *path;
395         const char *anchor = NULL, *target = NULL;
396         apr_pool_t *temp_pool;
397         PyObject *ret, *py_path;
398
399         if (!PyArg_ParseTuple(args, "O", &py_path))
400                 return NULL;
401
402         temp_pool = Pool(NULL);
403         if (temp_pool == NULL) {
404                 return NULL;
405         }
406
407         path = py_object_to_svn_dirent(py_path, temp_pool);
408         if (path == NULL) {
409                 apr_pool_destroy(temp_pool);
410                 return NULL;
411         }
412
413         RUN_SVN_WITH_POOL(temp_pool,
414                   svn_wc_get_actual_target(path,
415                                                                    &anchor, &target, temp_pool));
416
417         ret = Py_BuildValue("(ss)", anchor, target);
418
419         apr_pool_destroy(temp_pool);
420
421         return ret;
422 }
423
424 /**
425  * Determine the revision status of a specified working copy.
426  *
427  * :return: Tuple with minimum and maximum revnums found, whether the
428  * working copy was switched and whether it was modified.
429  */
430 static PyObject *revision_status(PyObject *self, PyObject *args, PyObject *kwargs)
431 {
432         char *kwnames[] = { "wc_path", "trail_url", "committed",  NULL };
433         const char *wc_path;
434         char *trail_url=NULL;
435         bool committed=false;
436         PyObject *ret, *py_wc_path;
437         svn_wc_revision_status_t *revstatus;
438         apr_pool_t *temp_pool;
439
440         if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|zb", kwnames, &py_wc_path,
441                                                                          &trail_url, &committed))
442                 return NULL;
443
444         temp_pool = Pool(NULL);
445         if (temp_pool == NULL) {
446                 return NULL;
447         }
448
449         wc_path = py_object_to_svn_dirent(py_wc_path, temp_pool);
450         if (wc_path == NULL) {
451                 apr_pool_destroy(temp_pool);
452                 return NULL;
453         }
454         RUN_SVN_WITH_POOL(temp_pool,
455                         svn_wc_revision_status(
456                                 &revstatus, wc_path, trail_url,
457                                  committed, py_cancel_check, NULL, temp_pool));
458         ret = Py_BuildValue("(llbb)", revstatus->min_rev, revstatus->max_rev,
459                         revstatus->switched, revstatus->modified);
460         apr_pool_destroy(temp_pool);
461         return ret;
462 }
463
464 static PyObject *is_normal_prop(PyObject *self, PyObject *args)
465 {
466         char *name;
467
468         if (!PyArg_ParseTuple(args, "s", &name))
469                 return NULL;
470
471         return PyBool_FromLong(svn_wc_is_normal_prop(name));
472 }
473
474 static PyObject *is_adm_dir(PyObject *self, PyObject *args)
475 {
476         char *name;
477         apr_pool_t *pool;
478         svn_boolean_t ret;
479
480         if (!PyArg_ParseTuple(args, "s", &name))
481                 return NULL;
482
483         pool = Pool(NULL);
484         if (pool == NULL)
485                 return NULL;
486
487         ret = svn_wc_is_adm_dir(name, pool);
488
489         apr_pool_destroy(pool);
490
491         return PyBool_FromLong(ret);
492 }
493
494 static PyObject *is_wc_prop(PyObject *self, PyObject *args)
495 {
496         char *name;
497
498         if (!PyArg_ParseTuple(args, "s", &name))
499                 return NULL;
500
501         return PyBool_FromLong(svn_wc_is_wc_prop(name));
502 }
503
504 static PyObject *is_entry_prop(PyObject *self, PyObject *args)
505 {
506         char *name;
507
508         if (!PyArg_ParseTuple(args, "s", &name))
509                 return NULL;
510
511         return PyBool_FromLong(svn_wc_is_entry_prop(name));
512 }
513
514 static PyObject *get_adm_dir(PyObject *self)
515 {
516         apr_pool_t *pool;
517         PyObject *ret;
518         const char *dir;
519         pool = Pool(NULL);
520         if (pool == NULL)
521                 return NULL;
522         dir = svn_wc_get_adm_dir(pool);
523         ret = py_object_from_svn_abspath(dir);
524         apr_pool_destroy(pool);
525         return ret;
526 }
527
528 static PyObject *set_adm_dir(PyObject *self, PyObject *args)
529 {
530         apr_pool_t *temp_pool;
531         char *name;
532         PyObject *py_name;
533
534         if (!PyArg_ParseTuple(args, "O", &py_name))
535                 return NULL;
536
537         temp_pool = Pool(NULL);
538         if (temp_pool == NULL)
539                 return NULL;
540         name = py_object_to_svn_string(py_name, temp_pool);
541         if (name == NULL) {
542                 apr_pool_destroy(temp_pool);
543                 return NULL;
544         }
545         RUN_SVN_WITH_POOL(temp_pool, svn_wc_set_adm_dir(name, temp_pool));
546         apr_pool_destroy(temp_pool);
547         Py_RETURN_NONE;
548 }
549
550 static PyObject *get_pristine_copy_path(PyObject *self, PyObject *args)
551 {
552         apr_pool_t *pool;
553         const char *pristine_path;
554         const char *path;
555         PyObject *py_path;
556         PyObject *ret;
557
558         if (!PyArg_ParseTuple(args, "O", &py_path))
559                 return NULL;
560
561         pool = Pool(NULL);
562         if (pool == NULL)
563                 return NULL;
564
565         path = py_object_to_svn_abspath(py_path, pool);
566         if (path == NULL) {
567                 apr_pool_destroy(pool);
568                 return NULL;
569         }
570
571         PyErr_WarnEx(PyExc_DeprecationWarning, "get_pristine_copy_path is deprecated. Use get_pristine_contents instead.", 2);
572         RUN_SVN_WITH_POOL(pool,
573                   svn_wc_get_pristine_copy_path(path,
574                                                                                 &pristine_path, pool));
575         ret = py_object_from_svn_abspath(pristine_path);
576         apr_pool_destroy(pool);
577         return ret;
578 }
579
580 static PyObject *get_pristine_contents(PyObject *self, PyObject *args)
581 {
582         const char *path;
583         apr_pool_t *temp_pool;
584         PyObject *py_path;
585 #if ONLY_SINCE_SVN(1, 6)
586         StreamObject *ret;
587         apr_pool_t *stream_pool;
588         svn_stream_t *stream;
589 #else
590 #if PY_MAJOR_VERSION >= 3
591         int fd;
592 #endif
593         PyObject *ret;
594         const char *pristine_path;
595 #endif
596
597         if (!PyArg_ParseTuple(args, "O", &py_path))
598                 return NULL;
599
600 #if ONLY_SINCE_SVN(1, 6)
601         stream_pool = Pool(NULL);
602         if (stream_pool == NULL)
603                 return NULL;
604
605         temp_pool = Pool(stream_pool);
606         if (temp_pool == NULL) {
607                 apr_pool_destroy(stream_pool);
608                 return NULL;
609         }
610 #else
611         temp_pool = Pool(NULL);
612         if (temp_pool == NULL) {
613                 return NULL;
614         }
615 #endif
616
617         path = py_object_to_svn_abspath(py_path, temp_pool);
618         if (path == NULL) {
619                 apr_pool_destroy(temp_pool);
620                 return NULL;
621         }
622
623 #if ONLY_SINCE_SVN(1, 6)
624         RUN_SVN_WITH_POOL(stream_pool, svn_wc_get_pristine_contents(&stream, path, stream_pool, temp_pool));
625         apr_pool_destroy(temp_pool);
626
627         if (stream == NULL) {
628                 apr_pool_destroy(stream_pool);
629                 Py_RETURN_NONE;
630         }
631
632         ret = PyObject_New(StreamObject, &Stream_Type);
633         if (ret == NULL)
634                 return NULL;
635
636         ret->pool = stream_pool;
637         ret->closed = FALSE;
638         ret->stream = stream;
639
640         return (PyObject *)ret;
641 #else
642         temp_pool = Pool(NULL);
643         if (temp_pool == NULL)
644                 return NULL;
645         RUN_SVN_WITH_POOL(temp_pool, svn_wc_get_pristine_copy_path(path, &pristine_path, temp_pool));
646 #if PY_MAJOR_VERSION >= 3
647         fd = open(pristine_path, O_RDONLY);
648         if (fd < 0) {
649                 PyErr_SetFromErrno(PyExc_IOError);
650                 apr_pool_destroy(temp_pool);
651                 return NULL;
652         }
653         ret = PyFile_FromFd(fd, pristine_path, "rb", -1, NULL, NULL, NULL, true);
654 #else
655         ret = PyFile_FromString((char *)pristine_path, "rb");
656 #endif
657         apr_pool_destroy(temp_pool);
658         return ret;
659 #endif
660 }
661
662 static PyObject *ensure_adm(PyObject *self, PyObject *args, PyObject *kwargs)
663 {
664         const char *path;
665         char *uuid, *url = NULL;
666         PyObject *py_path;
667         char *repos = NULL;
668         long rev = -1;
669         apr_pool_t *pool;
670         char *kwnames[] = { "path", "uuid", "url", "repos", "rev", "depth", NULL };
671         int depth = svn_depth_infinity;
672
673         if (!PyArg_ParseTupleAndKeywords(args, kwargs, "Oss|sli", kwnames,
674                                                                          &py_path, &uuid, &url, &repos, &rev, &depth))
675                 return NULL;
676
677         pool = Pool(NULL);
678         if (pool == NULL) {
679                 return NULL;
680         }
681
682         path = py_object_to_svn_dirent(py_path, pool);
683         if (path == NULL) {
684                 apr_pool_destroy(pool);
685                 return NULL;
686         }
687
688 #if ONLY_SINCE_SVN(1, 5)
689         RUN_SVN_WITH_POOL(pool,
690                                           svn_wc_ensure_adm3(path,
691                                                                                  uuid, url, repos, rev, depth, pool));
692 #else
693         if (depth != svn_depth_infinity) {
694                 PyErr_SetString(PyExc_NotImplementedError,
695                                                 "depth != infinity not supported with svn < 1.5");
696                 apr_pool_destroy(pool);
697                 return NULL;
698         }
699         RUN_SVN_WITH_POOL(pool,
700                                           svn_wc_ensure_adm2(path,
701                                                                                  uuid, url, repos, rev, pool));
702 #endif
703         apr_pool_destroy(pool);
704         Py_RETURN_NONE;
705 }
706
707 static PyObject *check_wc(PyObject *self, PyObject *args)
708 {
709         const char *path;
710         apr_pool_t *pool;
711         int wc_format;
712         PyObject *py_path;
713
714         if (!PyArg_ParseTuple(args, "O", &py_path))
715                 return NULL;
716
717         pool = Pool(NULL);
718         if (pool == NULL) {
719                 return NULL;
720         }
721
722         path = py_object_to_svn_dirent(py_path, pool);
723         if (path == NULL) {
724                 apr_pool_destroy(pool);
725                 return NULL;
726         }
727
728         RUN_SVN_WITH_POOL(pool, svn_wc_check_wc(path, &wc_format, pool));
729         apr_pool_destroy(pool);
730         return PyLong_FromLong(wc_format);
731 }
732
733 static PyObject *cleanup_wc(PyObject *self, PyObject *args, PyObject *kwargs)
734 {
735         const char *path;
736         char *diff3_cmd = NULL;
737         char *kwnames[] = { "path", "diff3_cmd", NULL };
738         apr_pool_t *temp_pool;
739         PyObject *py_path;
740
741         if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|z", kwnames,
742                                                                          &py_path, &diff3_cmd))
743                 return NULL;
744
745         temp_pool = Pool(NULL);
746         if (temp_pool == NULL) {
747                 return NULL;
748         }
749
750         path = py_object_to_svn_dirent(py_path, temp_pool);
751         if (path == NULL) {
752                 apr_pool_destroy(temp_pool);
753                 return NULL;
754         }
755
756         RUN_SVN_WITH_POOL(temp_pool,
757                                 svn_wc_cleanup2(path, diff3_cmd, py_cancel_check, NULL,
758                                                                 temp_pool));
759
760         apr_pool_destroy(temp_pool);
761
762         Py_RETURN_NONE;
763 }
764
765 static PyObject *match_ignore_list(PyObject *self, PyObject *args)
766 {
767 #if ONLY_SINCE_SVN(1, 5)
768         char *str;
769         PyObject *py_list;
770         apr_array_header_t *list;
771         apr_pool_t *temp_pool;
772         svn_boolean_t ret;
773
774         if (!PyArg_ParseTuple(args, "sO", &str, &py_list))
775                 return NULL;
776
777         temp_pool = Pool(NULL);
778
779         if (!string_list_to_apr_array(temp_pool, py_list, &list)) {
780                 apr_pool_destroy(temp_pool);
781                 return NULL;
782         }
783
784         ret = svn_wc_match_ignore_list(str, list, temp_pool);
785
786         apr_pool_destroy(temp_pool);
787
788         return PyBool_FromLong(ret);
789 #else
790         PyErr_SetNone(PyExc_NotImplementedError);
791         return NULL;
792 #endif
793 }
794
795 static PyMethodDef wc_methods[] = {
796     { "check_wc", check_wc, METH_VARARGS, "check_wc(path) -> version\n"
797         "Check whether path contains a Subversion working copy\n"
798             "return the workdir version"},
799     { "cleanup", (PyCFunction)cleanup_wc,
800         METH_VARARGS|METH_KEYWORDS, "cleanup(path, diff3_cmd=None)\n" },
801     { "ensure_adm", (PyCFunction)ensure_adm, METH_KEYWORDS|METH_VARARGS,
802         "ensure_adm(path, uuid, url, repos=None, rev=None)" },
803     { "get_adm_dir", (PyCFunction)get_adm_dir, METH_NOARGS,
804         "get_adm_dir() -> name" },
805     { "set_adm_dir", (PyCFunction)set_adm_dir, METH_VARARGS,
806         "set_adm_dir(name)" },
807     { "get_pristine_copy_path", get_pristine_copy_path, METH_VARARGS,
808         "get_pristine_copy_path(path) -> path" },
809     { "get_pristine_contents", get_pristine_contents, METH_VARARGS,
810         "get_pristine_contents(path) -> stream" },
811     { "is_adm_dir", is_adm_dir, METH_VARARGS,
812         "is_adm_dir(name) -> bool" },
813     { "is_normal_prop", is_normal_prop, METH_VARARGS,
814         "is_normal_prop(name) -> bool" },
815     { "is_entry_prop", is_entry_prop, METH_VARARGS,
816         "is_entry_prop(name) -> bool" },
817     { "is_wc_prop", is_wc_prop, METH_VARARGS,
818         "is_wc_prop(name) -> bool" },
819     { "revision_status", (PyCFunction)revision_status,
820         METH_KEYWORDS|METH_VARARGS,
821         "revision_status(wc_path, trail_url=None, committed=False)"
822             "-> (min_rev, max_rev, switched, modified)" },
823     { "version", (PyCFunction)version, METH_NOARGS,
824         "version() -> (major, minor, patch, tag)\n\n"
825             "Version of libsvn_wc currently used."
826     },
827     { "api_version", (PyCFunction)api_version, METH_NOARGS,
828         "api_version() -> (major, minor, patch, tag)\n\n"
829             "Version of libsvn_wc Subvertpy was compiled against." },
830     { "match_ignore_list", (PyCFunction)match_ignore_list, METH_VARARGS,
831         "match_ignore_list(str, patterns) -> bool" },
832     { "get_actual_target", (PyCFunction)get_actual_target, METH_VARARGS,
833         "get_actual_target(path) -> (anchor, target)" },
834     { NULL, }
835 };
836
837 static void committed_queue_dealloc(PyObject *self)
838 {
839         apr_pool_destroy(((CommittedQueueObject *)self)->pool);
840         PyObject_Del(self);
841 }
842
843 static PyObject *committed_queue_repr(PyObject *self)
844 {
845         CommittedQueueObject *cqobj = (CommittedQueueObject *)self;
846
847         return PyRepr_FromFormat("<wc.CommittedQueue at 0x%p>", cqobj->queue);
848 }
849
850 static PyObject *committed_queue_init(PyTypeObject *self, PyObject *args, PyObject *kwargs)
851 {
852         CommittedQueueObject *ret;
853         char *kwnames[] = { NULL };
854
855         if (!PyArg_ParseTupleAndKeywords(args, kwargs, "", kwnames))
856                 return NULL;
857
858         ret = PyObject_New(CommittedQueueObject, &CommittedQueue_Type);
859         if (ret == NULL)
860                 return NULL;
861
862         ret->pool = Pool(NULL);
863         if (ret->pool == NULL)
864                 return NULL;
865         ret->queue = svn_wc_committed_queue_create(ret->pool);
866         if (ret->queue == NULL) {
867                 PyObject_Del(ret);
868                 PyErr_NoMemory();
869                 return NULL;
870         }
871
872         return (PyObject *)ret;
873 }
874
875 static PyObject *committed_queue_queue(CommittedQueueObject *self, PyObject *args, PyObject *kwargs)
876 {
877         const char *path;
878         PyObject *admobj;
879         PyObject *py_wcprop_changes = Py_None, *py_path;
880     svn_wc_adm_access_t *adm;
881         bool remove_lock = false, remove_changelist = false;
882         char *md5_digest = NULL, *sha1_digest = NULL;
883         bool recurse = false;
884         apr_pool_t *temp_pool;
885         apr_array_header_t *wcprop_changes;
886         int md5_digest_len, sha1_digest_len;
887         char *kwnames[] = { "path", "adm", "recurse", "wcprop_changes", "remove_lock", "remove_changelist", "md5_digest", "sha1_digest", NULL };
888
889         if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OO!|bObbz#z#", kwnames,
890                                                                          &py_path, &Adm_Type, &admobj,
891                                                   &recurse, &py_wcprop_changes, &remove_lock,
892                                                   &remove_changelist, &md5_digest, &md5_digest_len,
893                                                   &sha1_digest, &sha1_digest_len))
894                 return NULL;
895
896         temp_pool = Pool(NULL);
897         if (temp_pool == NULL)
898                 return NULL;
899
900         if (!py_dict_to_wcprop_changes(py_wcprop_changes, self->pool, &wcprop_changes)) {
901                 apr_pool_destroy(temp_pool);
902                 return NULL;
903         }
904
905         path = py_object_to_svn_abspath(py_path, self->pool);
906         if (path == NULL) {
907                 apr_pool_destroy(temp_pool);
908                 return NULL;
909         }
910
911         if (md5_digest != NULL) {
912                 if (md5_digest_len != APR_MD5_DIGESTSIZE) {
913                         PyErr_SetString(PyExc_ValueError, "Invalid size for md5 digest");
914                         apr_pool_destroy(temp_pool);
915                         return NULL;
916                 }
917                 md5_digest = apr_pstrdup(temp_pool, md5_digest);
918                 if (md5_digest == NULL) {
919                         PyErr_NoMemory();
920                         return NULL;
921                 }
922         }
923
924         if (sha1_digest != NULL) {
925                 if (sha1_digest_len != APR_SHA1_DIGESTSIZE) {
926                         PyErr_SetString(PyExc_ValueError, "Invalid size for sha1 digest");
927                         apr_pool_destroy(temp_pool);
928                         return NULL;
929                 }
930                 sha1_digest = apr_pstrdup(temp_pool, sha1_digest);
931                 if (sha1_digest == NULL) {
932                         PyErr_NoMemory();
933                         return NULL;
934                 }
935         }
936
937     adm = PyObject_GetAdmAccess(admobj);
938
939 #if ONLY_SINCE_SVN(1, 6)
940         {
941         svn_checksum_t svn_checksum, *svn_checksum_p = &svn_checksum;
942
943         if (sha1_digest != NULL) {
944                 svn_checksum.digest = (unsigned char *)sha1_digest;
945                 svn_checksum.kind = svn_checksum_sha1;
946         } else if (md5_digest != NULL) {
947                 svn_checksum.digest = (unsigned char *)md5_digest;
948                 svn_checksum.kind = svn_checksum_md5;
949         } else {
950                 svn_checksum_p = NULL;
951         }
952         RUN_SVN_WITH_POOL(temp_pool,
953                 svn_wc_queue_committed2(self->queue, path, adm, recurse?TRUE:FALSE,
954                                                            wcprop_changes, remove_lock?TRUE:FALSE, remove_changelist?TRUE:FALSE,
955                                                            svn_checksum_p, temp_pool));
956         }
957 #else
958         RUN_SVN_WITH_POOL(temp_pool,
959                 svn_wc_queue_committed(&self->queue, path, adm, recurse?TRUE:FALSE,
960                                                            wcprop_changes, remove_lock?TRUE:FALSE, remove_changelist?TRUE:FALSE,
961                                                            (unsigned char *)md5_digest, temp_pool));
962 #endif
963
964         apr_pool_destroy(temp_pool);
965
966         Py_RETURN_NONE;
967 }
968
969 static PyMethodDef committed_queue_methods[] = {
970         { "queue", (PyCFunction)committed_queue_queue, METH_VARARGS|METH_KEYWORDS,
971                 "S.queue(path, adm, recurse=False, wcprop_changes=[], remove_lock=False, remove_changelist=False, digest=None)" },
972         { NULL }
973 };
974
975 PyTypeObject CommittedQueue_Type = {
976         PyVarObject_HEAD_INIT(NULL, 0)
977         "wc.CommittedQueue", /* const char *tp_name;  For printing, in format "<module>.<name>" */
978         sizeof(CommittedQueueObject),
979         0,/*    Py_ssize_t tp_basicsize, tp_itemsize;  For allocation */
980
981         /* Methods to implement standard operations */
982
983         committed_queue_dealloc, /*     destructor tp_dealloc;  */
984         NULL, /*        printfunc tp_print;     */
985         NULL, /*        getattrfunc tp_getattr; */
986         NULL, /*        setattrfunc tp_setattr; */
987         NULL, /*        cmpfunc tp_compare;     */
988         committed_queue_repr, /*        reprfunc tp_repr;       */
989
990         /* Method suites for standard classes */
991
992         NULL, /*        PyNumberMethods *tp_as_number;  */
993         NULL, /*        PySequenceMethods *tp_as_sequence;      */
994         NULL, /*        PyMappingMethods *tp_as_mapping;        */
995
996         /* More standard operations (here for binary compatibility) */
997
998         NULL, /*        hashfunc tp_hash;       */
999         NULL, /*        ternaryfunc tp_call;    */
1000         NULL, /*        reprfunc tp_str;        */
1001         NULL, /*        getattrofunc tp_getattro;       */
1002         NULL, /*        setattrofunc tp_setattro;       */
1003
1004         /* Functions to access object as input/output buffer */
1005         NULL, /*        PyBufferProcs *tp_as_buffer;    */
1006
1007         /* Flags to define presence of optional/expanded features */
1008         0, /*   long tp_flags;  */
1009
1010         "Committed queue", /*   const char *tp_doc;  Documentation string */
1011
1012         /* Assigned meaning in release 2.0 */
1013         /* call function for all accessible objects */
1014         NULL, /*        traverseproc tp_traverse;       */
1015
1016         /* delete references to contained objects */
1017         NULL, /*        inquiry tp_clear;       */
1018
1019         /* Assigned meaning in release 2.1 */
1020         /* rich comparisons */
1021         NULL, /*        richcmpfunc tp_richcompare;     */
1022
1023         /* weak reference enabler */
1024         0, /*   Py_ssize_t tp_weaklistoffset;   */
1025
1026         /* Added in release 2.2 */
1027         /* Iterators */
1028         NULL, /*        getiterfunc tp_iter;    */
1029         NULL, /*        iternextfunc tp_iternext;       */
1030
1031         /* Attribute descriptor and subclassing stuff */
1032         committed_queue_methods, /*     struct PyMethodDef *tp_methods; */
1033         NULL, /*        struct PyMemberDef *tp_members; */
1034         NULL, /*        struct PyGetSetDef *tp_getset;  */
1035         NULL, /*        struct _typeobject *tp_base;    */
1036         NULL, /*        PyObject *tp_dict;      */
1037         NULL, /*        descrgetfunc tp_descr_get;      */
1038         NULL, /*        descrsetfunc tp_descr_set;      */
1039         0, /*   Py_ssize_t tp_dictoffset;       */
1040         NULL, /*        initproc tp_init;       */
1041         NULL, /*        allocfunc tp_alloc;     */
1042         committed_queue_init, /*        newfunc tp_new; */
1043 };
1044
1045 #if ONLY_SINCE_SVN(1, 7)
1046 static PyTypeObject Context_Type;
1047
1048 typedef struct {
1049     PyObject_VAR_HEAD
1050     apr_pool_t *pool;
1051     svn_wc_context_t *context;
1052 } ContextObject;
1053
1054 static PyObject *py_wc_context_locked(PyObject *self, PyObject *args)
1055 {
1056     PyObject* py_path;
1057     const char *path;
1058     apr_pool_t *pool;
1059     svn_wc_context_t *wc_context = ((ContextObject *)self)->context;
1060     svn_boolean_t locked_here, locked;
1061
1062     if (!PyArg_ParseTuple(args, "O", &py_path))
1063         return NULL;
1064
1065     pool = Pool(NULL);
1066
1067     path = py_object_to_svn_abspath(py_path, pool);
1068     if (path == NULL) {
1069         apr_pool_destroy(pool);
1070         return NULL;
1071     }
1072
1073     RUN_SVN_WITH_POOL(pool, svn_wc_locked2(&locked_here, &locked, wc_context, path, pool));
1074
1075     apr_pool_destroy(pool);
1076
1077     return Py_BuildValue("(bb)", locked_here?true:false, locked?true:false);
1078 }
1079
1080 static PyObject *py_wc_context_check_wc(PyObject *self, PyObject *args)
1081 {
1082     PyObject* py_path;
1083     const char *path;
1084     apr_pool_t *pool;
1085     svn_wc_context_t *wc_context = ((ContextObject *)self)->context;
1086     int wc_format;
1087
1088     if (!PyArg_ParseTuple(args, "O", &py_path))
1089         return NULL;
1090
1091     pool = Pool(NULL);
1092
1093     path = py_object_to_svn_abspath(py_path, pool);
1094     if (path == NULL) {
1095         apr_pool_destroy(pool);
1096         return NULL;
1097     }
1098
1099     RUN_SVN_WITH_POOL(pool, svn_wc_check_wc2(&wc_format, wc_context, path, pool));
1100
1101     apr_pool_destroy(pool);
1102
1103 #if PY_MAJOR_VERSION >= 3
1104     return PyLong_FromLong(wc_format);
1105 #else
1106     return PyInt_FromLong(wc_format);
1107 #endif
1108 }
1109
1110 static PyObject *py_wc_context_text_modified_p2(PyObject *self, PyObject *args)
1111 {
1112     PyObject* py_path;
1113     const char *path;
1114     apr_pool_t *pool;
1115     svn_wc_context_t *wc_context = ((ContextObject *)self)->context;
1116     svn_boolean_t modified;
1117
1118     if (!PyArg_ParseTuple(args, "O", &py_path))
1119         return NULL;
1120
1121     pool = Pool(NULL);
1122
1123     path = py_object_to_svn_abspath(py_path, pool);
1124     if (path == NULL) {
1125         apr_pool_destroy(pool);
1126         return NULL;
1127     }
1128
1129     RUN_SVN_WITH_POOL(pool, svn_wc_text_modified_p2(&modified, wc_context,
1130                                                     path, FALSE, pool));
1131
1132     apr_pool_destroy(pool);
1133
1134     return PyBool_FromLong(modified);
1135 }
1136
1137 static PyObject *py_wc_context_props_modified_p2(PyObject *self, PyObject *args)
1138 {
1139     PyObject* py_path;
1140     const char *path;
1141     apr_pool_t *pool;
1142     svn_wc_context_t *wc_context = ((ContextObject *)self)->context;
1143     svn_boolean_t modified;
1144
1145     if (!PyArg_ParseTuple(args, "O", &py_path))
1146         return NULL;
1147
1148     pool = Pool(NULL);
1149
1150     path = py_object_to_svn_abspath(py_path, pool);
1151     if (path == NULL) {
1152         apr_pool_destroy(pool);
1153         return NULL;
1154     }
1155
1156     RUN_SVN_WITH_POOL(pool, svn_wc_props_modified_p2(&modified, wc_context,
1157                                                     path, pool));
1158
1159     apr_pool_destroy(pool);
1160
1161     return PyBool_FromLong(modified);
1162 }
1163
1164 static PyObject *py_wc_context_conflicted(PyObject *self, PyObject *args)
1165 {
1166     PyObject* py_path;
1167     const char *path;
1168     apr_pool_t *pool;
1169     svn_wc_context_t *wc_context = ((ContextObject *)self)->context;
1170     svn_boolean_t text_conflicted, props_conflicted, tree_conflicted;
1171
1172     if (!PyArg_ParseTuple(args, "O", &py_path))
1173         return NULL;
1174
1175     pool = Pool(NULL);
1176
1177     path = py_object_to_svn_abspath(py_path, pool);
1178     if (path == NULL) {
1179         apr_pool_destroy(pool);
1180         return NULL;
1181     }
1182
1183     RUN_SVN_WITH_POOL(pool, svn_wc_conflicted_p3(
1184          &text_conflicted, &props_conflicted, &tree_conflicted, wc_context,
1185          path, pool));
1186
1187     apr_pool_destroy(pool);
1188
1189     return Py_BuildValue("(bbb)", text_conflicted, props_conflicted, tree_conflicted);
1190 }
1191
1192 static PyObject *py_wc_context_crawl_revisions(PyObject *self, PyObject *args, PyObject *kwargs)
1193 {
1194     PyObject* py_path, *py_reporter;
1195     const char *path;
1196     apr_pool_t *pool;
1197     svn_wc_context_t *wc_context = ((ContextObject *)self)->context;
1198     char *kwnames[] = { "path", "reporter", "restore_files", "depth",
1199         "honor_depth_exclude", "depth_compatibility_trick", "use_commit_times",
1200         "cancel", "notify", NULL };
1201     bool restore_files = false;
1202     int depth = svn_depth_infinity;
1203     bool honor_depth_exclude = true;
1204     bool depth_compatibility_trick = false;
1205     bool use_commit_times = false;
1206     PyObject *notify = Py_None;
1207
1208     if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OO|bibbbOO", kwnames,
1209                                      &py_path, &py_reporter, &restore_files,
1210                                      &depth, &honor_depth_exclude,
1211                                      &depth_compatibility_trick,
1212                                      &use_commit_times, &notify)) {
1213         return NULL;
1214     }
1215
1216     pool = Pool(NULL);
1217
1218     path = py_object_to_svn_abspath(py_path, pool);
1219     if (path == NULL) {
1220         apr_pool_destroy(pool);
1221         return NULL;
1222     }
1223
1224     RUN_SVN_WITH_POOL(pool, svn_wc_crawl_revisions5(
1225          wc_context, path, &py_ra_reporter3, py_reporter, restore_files,
1226          depth, honor_depth_exclude, depth_compatibility_trick,
1227          use_commit_times, py_cancel_check, NULL, py_wc_notify_func, notify, pool));
1228
1229     apr_pool_destroy(pool);
1230
1231     Py_RETURN_NONE;
1232 }
1233
1234 static void context_done_handler(void *self)
1235 {
1236     PyObject *selfobj = (PyObject *)self;
1237
1238     Py_DECREF(selfobj);
1239 }
1240
1241 static PyObject *py_wc_context_get_update_editor(PyObject *self, PyObject *args, PyObject *kwargs)
1242 {
1243     char *kwnames[] = {
1244         "anchor_abspath", "target_basename", "use_commit_times", "depth",
1245         "depth_is_sticky", "allow_unver_obstructions", "adds_as_modification",
1246         "server_performs_filtering", "clean_checkout", "diff3_cmd",
1247         "preserved_exts", "dirents_func", "conflict_func", "external_func",
1248         "notify_func", NULL };
1249     const svn_delta_editor_t *editor;
1250     void *edit_baton;
1251     const char *anchor_abspath;
1252     char *target_basename;
1253     char *diff3_cmd = NULL;
1254     svn_wc_context_t *wc_context = ((ContextObject *)self)->context;
1255     bool use_commit_times = false;
1256     int depth = svn_depth_infinity;
1257     bool depth_is_sticky = false;
1258     bool allow_unver_obstructions = true;
1259     bool adds_as_modification = false;
1260     bool server_performs_filtering = false;
1261     bool clean_checkout = false;
1262     apr_array_header_t *preserved_exts = NULL;
1263     PyObject *py_preserved_exts = Py_None;
1264     PyObject *dirents_func = Py_None;
1265     PyObject *conflict_func = Py_None;
1266     PyObject *external_func = Py_None;
1267     PyObject *notify_func = Py_None;
1268     PyObject *py_anchor_abspath;
1269     apr_pool_t *result_pool, *scratch_pool;
1270     svn_error_t *err;
1271     svn_revnum_t target_revision;
1272
1273     if (!PyArg_ParseTupleAndKeywords(args, kwargs, "Os|bibbbbbzOOOOO", kwnames,
1274                                      &py_anchor_abspath, &target_basename,
1275                                      &use_commit_times, &depth,
1276                                      &depth_is_sticky,
1277                                      &allow_unver_obstructions,
1278                                      &adds_as_modification,
1279                                      &server_performs_filtering,
1280                                      &clean_checkout, &py_preserved_exts,
1281                                      &dirents_func, &conflict_func,
1282                                      &external_func, &notify_func)) {
1283         return NULL;
1284     }
1285
1286     if (conflict_func != Py_None) {
1287         // TODO
1288         PyErr_SetString(PyExc_NotImplementedError,
1289                         "conflict_func is not currently supported");
1290         return NULL;
1291     }
1292
1293     if (external_func != Py_None) {
1294         // TODO
1295         PyErr_SetString(PyExc_NotImplementedError,
1296                         "external_func is not currently supported");
1297         return NULL;
1298     }
1299
1300     if (dirents_func != Py_None) {
1301         // TODO
1302         PyErr_SetString(PyExc_NotImplementedError,
1303                         "dirents_func is not currently supported");
1304         return NULL;
1305     }
1306
1307     scratch_pool = Pool(NULL);
1308
1309     anchor_abspath = py_object_to_svn_abspath(py_anchor_abspath, scratch_pool);
1310
1311     if (py_preserved_exts != Py_None) {
1312         if (!string_list_to_apr_array(scratch_pool, py_preserved_exts, &preserved_exts)) {
1313             apr_pool_destroy(scratch_pool);
1314             return NULL;
1315         }
1316     }
1317
1318     result_pool = Pool(NULL);
1319
1320     Py_BEGIN_ALLOW_THREADS
1321     err = svn_wc_get_update_editor4(
1322              &editor, &edit_baton, &target_revision, wc_context,
1323              anchor_abspath, target_basename, use_commit_times, depth,
1324              depth_is_sticky, allow_unver_obstructions, adds_as_modification,
1325              server_performs_filtering, clean_checkout, diff3_cmd,
1326              preserved_exts, NULL, dirents_func, NULL, conflict_func, NULL,
1327              external_func, py_cancel_check, NULL, py_wc_notify_func,
1328              notify_func, result_pool, scratch_pool);
1329     Py_END_ALLOW_THREADS
1330
1331     apr_pool_destroy(scratch_pool);
1332
1333     if (err != NULL) {
1334         handle_svn_error(err);
1335         svn_error_clear(err);
1336         apr_pool_destroy(result_pool);
1337         return NULL;
1338     }
1339
1340     /* TODO: Also return target_revision ? */
1341     Py_INCREF(self);
1342     return new_editor_object(NULL, editor, edit_baton, result_pool, &Editor_Type,
1343                              context_done_handler, self, NULL);
1344 }
1345
1346 static PyObject *py_wc_context_ensure_adm(PyObject *self, PyObject *args,
1347                                           PyObject *kwargs)
1348 {
1349     ContextObject *context_obj = (ContextObject *)self;
1350     char *kwnames[] = {
1351         "local_abspath", "url", "repos_root_url", "repos_uuid",
1352         "revnum", "depth", NULL };
1353     char *local_abspath;
1354     char *url;
1355     char *repos_root_url;
1356     char *repos_uuid;
1357     int revnum;
1358     int depth = svn_depth_infinity;
1359     apr_pool_t *pool;
1360
1361     if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ssssi|i", kwnames,
1362                                      &local_abspath, &url, &repos_root_url,
1363                                      &repos_uuid, &revnum, &depth)) {
1364         return NULL;
1365     }
1366
1367     pool = Pool(NULL);
1368
1369     RUN_SVN_WITH_POOL(pool, svn_wc_ensure_adm4(context_obj->context,
1370                                                local_abspath, url,
1371                                                repos_root_url, repos_uuid,
1372                                                revnum, depth, pool));
1373
1374     apr_pool_destroy(pool);
1375
1376     Py_RETURN_NONE;
1377 }
1378
1379 typedef struct {
1380     PyObject_VAR_HEAD
1381     apr_pool_t *pool;
1382     svn_wc_status3_t status;
1383 } Status3Object;
1384
1385 static void status_dealloc(PyObject *self)
1386 {
1387     apr_pool_t *pool = ((Status3Object *)self)->pool;
1388     if (pool != NULL)
1389         apr_pool_destroy(pool);
1390     PyObject_Del(self);
1391 }
1392
1393 static PyMemberDef status_members[] = {
1394     { "kind", T_INT, offsetof(Status3Object, status.kind), READONLY,
1395         "The kind of node as recorded in the working copy." },
1396     { "depth", T_INT, offsetof(Status3Object, status.depth), READONLY,
1397         "The depth of the node as recorded in the working copy." },
1398     { "filesize", T_LONG, offsetof(Status3Object, status.filesize), READONLY,
1399         "The actual size of the working file on disk, or SVN_INVALID_FILESIZE"
1400         "if unknown (or if the item isn't a file at all)" },
1401     { "versioned", T_BOOL, offsetof(Status3Object, status.versioned), READONLY,
1402         "If the path is under version control, versioned is TRUE, "
1403         "otherwise FALSE." },
1404     { "repos_uuid", T_STRING, offsetof(Status3Object, status.repos_uuid), READONLY,
1405         "UUID of repository" },
1406     { "repos_root_url", T_STRING, offsetof(Status3Object, status.repos_root_url), READONLY,
1407         "Repository root URL" },
1408     { "repos_relpath", T_STRING, offsetof(Status3Object, status.repos_relpath), READONLY,
1409         "Relative path in repository" },
1410     /* TODO */
1411     { NULL }
1412 };
1413
1414 static PyTypeObject Status3_Type = {
1415     PyVarObject_HEAD_INIT(NULL, 0)
1416     "wc.Status", /*   const char *tp_name;  For printing, in format "<module>.<name>" */
1417     sizeof(Status3Object),
1418     0,/*    Py_ssize_t tp_basicsize, tp_itemsize;  For allocation */
1419
1420     /* Methods to implement standard operations */
1421
1422     status_dealloc, /*    destructor tp_dealloc;  */
1423     NULL, /*    printfunc tp_print; */
1424     NULL, /*    getattrfunc tp_getattr; */
1425     NULL, /*    setattrfunc tp_setattr; */
1426     NULL, /*    cmpfunc tp_compare; */
1427     NULL, /*    reprfunc tp_repr;   */
1428
1429     /* Method suites for standard classes */
1430
1431     NULL, /*    PyNumberMethods *tp_as_number;  */
1432     NULL, /*    PySequenceMethods *tp_as_sequence;  */
1433     NULL, /*    PyMappingMethods *tp_as_mapping;    */
1434
1435     /* More standard operations (here for binary compatibility) */
1436
1437     NULL, /*    hashfunc tp_hash;   */
1438     NULL, /*    ternaryfunc tp_call;    */
1439     NULL, /*    reprfunc tp_str;    */
1440     NULL, /*    getattrofunc tp_getattro;   */
1441     NULL, /*    setattrofunc tp_setattro;   */
1442
1443     /* Functions to access object as input/output buffer */
1444     NULL, /*    PyBufferProcs *tp_as_buffer;    */
1445
1446     /* Flags to define presence of optional/expanded features */
1447     0, /*   long tp_flags;  */
1448
1449     NULL, /*    const char *tp_doc;  Documentation string */
1450
1451     /* Assigned meaning in release 2.0 */
1452     /* call function for all accessible objects */
1453     NULL, /*    traverseproc tp_traverse;   */
1454
1455     /* delete references to contained objects */
1456     NULL, /*    inquiry tp_clear;   */
1457
1458     /* Assigned meaning in release 2.1 */
1459     /* rich comparisons */
1460     NULL, /*    richcmpfunc tp_richcompare; */
1461
1462     /* weak reference enabler */
1463     0, /*   Py_ssize_t tp_weaklistoffset;   */
1464
1465     /* Added in release 2.2 */
1466     /* Iterators */
1467     NULL, /*    getiterfunc tp_iter;    */
1468     NULL, /*    iternextfunc tp_iternext;   */
1469
1470     /* Attribute descriptor and subclassing stuff */
1471     NULL, /*    struct PyMethodDef *tp_methods; */
1472     status_members, /*    struct PyMemberDef *tp_members; */
1473     NULL, /* struct PyGetSetDef *tp_getsetters; */
1474 };
1475
1476 static PyObject *py_wc_status(PyObject *self, PyObject *args, PyObject *kwargs)
1477 {
1478     ContextObject *context_obj = (ContextObject *)self;
1479     char *kwnames[] = {"path", NULL};
1480     PyObject *py_path;
1481     Status3Object *ret;
1482     const char *path;
1483     apr_pool_t *scratch_pool, *result_pool;
1484     svn_wc_status3_t* status;
1485
1486     if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O", kwnames, &py_path)) {
1487         return NULL;
1488     }
1489
1490     result_pool = Pool(NULL);
1491     if (result_pool == NULL) {
1492         return NULL;
1493     }
1494     scratch_pool = Pool(result_pool);
1495     if (scratch_pool == NULL) {
1496         apr_pool_destroy(result_pool);
1497         return NULL;
1498     }
1499
1500     path = py_object_to_svn_abspath(py_path, scratch_pool);
1501     if (path == NULL) {
1502         apr_pool_destroy(result_pool);
1503         return NULL;
1504     }
1505
1506     RUN_SVN_WITH_POOL(result_pool,
1507                       svn_wc_status3(&status, context_obj->context, path,
1508                                      result_pool, scratch_pool));
1509
1510     apr_pool_destroy(scratch_pool);
1511
1512     ret = PyObject_New(Status3Object, &Status3_Type);
1513     if (ret == NULL) {
1514         apr_pool_destroy(result_pool);
1515         return NULL;
1516     }
1517     ret->pool = result_pool;
1518     ret->status = *status;
1519     return (PyObject *)ret;
1520 }
1521
1522 static svn_error_t *py_status_receiver(void *baton, const char *local_abspath,
1523                                        const svn_wc_status3_t *status,
1524                                        apr_pool_t *scratch_pool)
1525 {
1526     Status3Object *py_status;
1527     PyObject *ret;
1528     PyGILState_STATE state;
1529
1530     if (baton == Py_None)
1531         return NULL;
1532
1533     state = PyGILState_Ensure();
1534
1535     py_status = PyObject_New(Status3Object, &Status3_Type);
1536     if (py_status == NULL) {
1537         PyGILState_Release(state);
1538         return py_svn_error();
1539     }
1540     py_status->pool = Pool(NULL);
1541     py_status->status = *svn_wc_dup_status3(status, py_status->pool);
1542
1543     ret = PyObject_CallFunction((PyObject *)baton, "sO", local_abspath, py_status);
1544     Py_DECREF(py_status);
1545
1546     if (ret == NULL) {
1547         PyGILState_Release(state);
1548         return py_svn_error();
1549     }
1550
1551     Py_DECREF(ret);
1552     PyGILState_Release(state);
1553
1554     return NULL;
1555 }
1556
1557 static PyObject *py_wc_walk_status(PyObject *self, PyObject *args, PyObject *kwargs)
1558 {
1559     ContextObject *context_obj = (ContextObject *)self;
1560     char *kwnames[] = {"path", "receiver", "depth", "get_all", "no_ignore",
1561         "ignore_text_mode", "ignore_patterns", NULL};
1562     PyObject *py_path;
1563     const char *path;
1564     int depth = svn_depth_infinity;
1565     bool get_all = true;
1566     bool no_ignore = false;
1567     bool ignore_text_mode = false;
1568     PyObject *py_ignore_patterns = Py_None;
1569     PyObject *status_func;
1570     apr_array_header_t *ignore_patterns;
1571     apr_pool_t *pool;
1572
1573     if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OO|ibbbOO", kwnames,
1574                                      &py_path, &status_func, &depth, &get_all, &no_ignore,
1575                                      &ignore_text_mode, &py_ignore_patterns)) {
1576         return NULL;
1577     }
1578
1579     pool = Pool(NULL);
1580
1581     path = py_object_to_svn_abspath(py_path, pool);
1582     if (path == NULL) {
1583         apr_pool_destroy(pool);
1584         return NULL;
1585     }
1586
1587     if (py_ignore_patterns == Py_None) {
1588         ignore_patterns = NULL;
1589     } else {
1590         if (!string_list_to_apr_array(pool, py_ignore_patterns, &ignore_patterns)) {
1591             apr_pool_destroy(pool);
1592             return NULL;
1593         }
1594     }
1595
1596     RUN_SVN_WITH_POOL(pool,
1597                       svn_wc_walk_status(context_obj->context, path, depth,
1598                                          get_all, no_ignore, ignore_text_mode,
1599                                          ignore_patterns, py_status_receiver,
1600                                          status_func, py_cancel_check, NULL,
1601                                          pool));
1602
1603     apr_pool_destroy(pool);
1604
1605     Py_RETURN_NONE;
1606 }
1607
1608 static svn_lock_t *py_object_to_svn_lock(PyObject *py_lock, apr_pool_t *pool)
1609 {
1610         LockObject* lockobj = (LockObject *)py_lock;
1611         return lockobj->lock;
1612 }
1613
1614 static PyObject *py_wc_add_lock(PyObject *self, PyObject *args, PyObject *kwargs)
1615 {
1616     ContextObject *context_obj = (ContextObject *)self;
1617     PyObject *py_path, *py_lock;
1618     svn_lock_t *lock;
1619     char *kwnames[] = { "path", "lock", NULL };
1620     const char *path;
1621     apr_pool_t *scratch_pool;
1622
1623     if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OO!", kwnames, &py_path, &Lock_Type,
1624                                      &py_lock)) {
1625         return NULL;
1626     }
1627
1628     scratch_pool = Pool(NULL);
1629
1630     path = py_object_to_svn_abspath(py_path, scratch_pool);
1631     if (path == NULL) {
1632         apr_pool_destroy(scratch_pool);
1633         return NULL;
1634     }
1635
1636     lock = py_object_to_svn_lock(py_lock, scratch_pool);
1637     if (lock == NULL) {
1638         apr_pool_destroy(scratch_pool);
1639         return NULL;
1640     }
1641
1642     RUN_SVN_WITH_POOL(scratch_pool,
1643                       svn_wc_add_lock2(context_obj->context, path, lock, scratch_pool));
1644
1645     apr_pool_destroy(scratch_pool);
1646
1647     Py_RETURN_NONE;
1648 }
1649
1650 static PyObject *py_wc_remove_lock(PyObject *self, PyObject *args, PyObject *kwargs)
1651 {
1652     ContextObject *context_obj = (ContextObject *)self;
1653     char *kwnames[] = { "path", NULL };
1654     PyObject *py_path;
1655     const char *path;
1656     apr_pool_t *scratch_pool;
1657
1658     if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O", kwnames, &py_path)) {
1659         return NULL;
1660     }
1661
1662     scratch_pool = Pool(NULL);
1663
1664     path = py_object_to_svn_abspath(py_path, scratch_pool);
1665     if (path == NULL) {
1666         apr_pool_destroy(scratch_pool);
1667         return NULL;
1668     }
1669
1670     RUN_SVN_WITH_POOL(scratch_pool,
1671                       svn_wc_remove_lock2(context_obj->context, path,
1672                                           scratch_pool));
1673
1674     apr_pool_destroy(scratch_pool);
1675     Py_RETURN_NONE;
1676 }
1677
1678 static PyObject *py_wc_add_from_disk(PyObject *self, PyObject *args, PyObject *kwargs)
1679 {
1680     ContextObject *context_obj = (ContextObject *)self;
1681     char *kwnames[] = {"path", "props", "skip_checks", "notify", NULL };
1682     PyObject *py_path;
1683     const char *path;
1684     bool skip_checks = false;
1685     PyObject *py_props = Py_None;
1686     PyObject *notify_func = Py_None;
1687     apr_pool_t *pool;
1688     apr_hash_t *props;
1689
1690     if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|ObO", kwnames,
1691                                      &py_path, &py_props, &skip_checks, &notify_func)) {
1692         return NULL;
1693     }
1694
1695     pool = Pool(NULL);
1696     if (pool == NULL) {
1697         return NULL;
1698     }
1699
1700     path = py_object_to_svn_abspath(py_path, pool);
1701     if (path == NULL) {
1702         apr_pool_destroy(pool);
1703         return NULL;
1704     }
1705
1706     if (py_props == Py_None) {
1707         props = NULL;
1708     } else {
1709         props = prop_dict_to_hash(pool, py_props);
1710         if (props == NULL) {
1711             apr_pool_destroy(pool);
1712             return NULL;
1713         }
1714     }
1715
1716 #if ONLY_SINCE_SVN(1, 9)
1717     RUN_SVN_WITH_POOL(
1718             pool, svn_wc_add_from_disk3(
1719                     context_obj->context, path, props, skip_checks,
1720                     notify_func == Py_None?NULL:py_wc_notify_func,
1721                     notify_func, pool));
1722 #else
1723     if (props != NULL) {
1724         PyErr_SetString(PyExc_NotImplementedError,
1725                         "props argument only supported on svn >= 1.9");
1726         apr_pool_destroy(pool);
1727         return NULL;
1728     }
1729
1730     if (skip_checks) {
1731         PyErr_SetString(PyExc_NotImplementedError,
1732                         "skip_checks argument only supported on svn >= 1.9");
1733         apr_pool_destroy(pool);
1734         return NULL;
1735     }
1736     RUN_SVN_WITH_POOL(
1737             pool, svn_wc_add_from_disk(
1738                     context_obj->context, path,
1739                     notify_func == Py_None?NULL:py_wc_notify_func,
1740                     notify_func, pool));
1741 #endif
1742
1743     apr_pool_destroy(pool);
1744
1745     Py_RETURN_NONE;
1746 }
1747
1748 static PyObject *py_wc_get_prop_diffs(PyObject *self, PyObject *args, PyObject *kwargs)
1749 {
1750     ContextObject *context_obj = (ContextObject *)self;
1751     PyObject *py_path, *py_orig_props, *py_propchanges;
1752     apr_pool_t *pool;
1753     char *kwnames[] = {"path", NULL};
1754     apr_hash_t *original_props;
1755     apr_array_header_t *propchanges;
1756     const char *path;
1757
1758     if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O", kwnames, &py_path)) {
1759         return NULL;
1760     }
1761
1762     pool = Pool(NULL);
1763
1764     path = py_object_to_svn_abspath(py_path, pool);
1765     if (path == NULL) {
1766         apr_pool_destroy(pool);
1767         return NULL;
1768     }
1769
1770     RUN_SVN_WITH_POOL(pool, svn_wc_get_prop_diffs2(&propchanges,
1771                                                    &original_props,
1772                                                    context_obj->context,
1773                                                    path, pool, pool));
1774
1775     py_orig_props = prop_hash_to_dict(original_props);
1776     if (py_orig_props == NULL) {
1777         apr_pool_destroy(pool);
1778         return NULL;
1779     }
1780
1781     py_propchanges = propchanges_to_list(propchanges);
1782     if (py_propchanges == NULL) {
1783         apr_pool_destroy(pool);
1784         Py_DECREF(py_propchanges);
1785         return NULL;
1786     }
1787
1788     apr_pool_destroy(pool);
1789
1790     return Py_BuildValue("NN", py_orig_props, py_propchanges);
1791 }
1792
1793 static PyMethodDef context_methods[] = {
1794     { "locked", py_wc_context_locked, METH_VARARGS,
1795         "locked(path) -> (locked_here, locked)\n"
1796         "Check whether a patch is locked."},
1797     { "check_wc", py_wc_context_check_wc, METH_VARARGS,
1798         "check_wc(path) -> wc_format\n"
1799         "Check format version of a working copy." },
1800     { "text_modified", py_wc_context_text_modified_p2, METH_VARARGS,
1801         "text_modified(path) -> bool\n"
1802         "Check whether text of a file is modified against base." },
1803     { "props_modified", py_wc_context_props_modified_p2, METH_VARARGS,
1804         "props_modified(path) -> bool\n"
1805         "Check whether props of a file are modified against base." },
1806     { "conflicted", py_wc_context_conflicted, METH_VARARGS,
1807         "conflicted(path) -> (text_conflicted, prop_conflicted, "
1808             "tree_conflicted)\n"
1809         "Check whether a path is conflicted." },
1810     { "crawl_revisions", (PyCFunction)py_wc_context_crawl_revisions,
1811         METH_VARARGS|METH_KEYWORDS,
1812         "crawl_revisions(path, reporter, restore_files, depth, "
1813             "honor_depth_exclude, depth_compatibility_trick, "
1814             "use_commit_time, notify)\n"
1815         "Do a depth-first crawl of the working copy." },
1816     { "get_update_editor",
1817         (PyCFunction)py_wc_context_get_update_editor,
1818         METH_VARARGS|METH_KEYWORDS,
1819         "get_update_editor(anchor_abspath, target_basename, use_commit_time, "
1820             "depth, depth_is_sticky, allow_unver_obstructions, "
1821             "adds_as_modification, server_performs_filtering, clean_checkout, "
1822             "diff3_cmd, dirent_func=None, conflict_func=None, "
1823             "external_func=None) -> target_revnum" },
1824     { "ensure_adm",
1825         (PyCFunction)py_wc_context_ensure_adm,
1826         METH_VARARGS|METH_KEYWORDS,
1827         "ensure_adm(local_abspath, url, repos_root_url, repos_uuid, revnum, depth)" },
1828     { "status",
1829         (PyCFunction)py_wc_status,
1830         METH_VARARGS|METH_KEYWORDS,
1831         "status(path) -> status" },
1832     { "walk_status",
1833         (PyCFunction)py_wc_walk_status,
1834         METH_VARARGS|METH_KEYWORDS,
1835         "walk_status(path, receiver, depth=DEPTH_INFINITY, get_all=True, "
1836             "no_ignore=False, ignore_text_mode=False, ignore_patterns=None)\n" },
1837     { "add_lock",
1838         (PyCFunction)py_wc_add_lock,
1839         METH_VARARGS|METH_KEYWORDS,
1840         "add_lock(path, lock)" },
1841     { "remove_lock",
1842         (PyCFunction)py_wc_remove_lock,
1843         METH_VARARGS|METH_KEYWORDS,
1844         "remove_lock(path)" },
1845     { "add_from_disk",
1846         (PyCFunction)py_wc_add_from_disk,
1847         METH_VARARGS|METH_KEYWORDS,
1848         "add_from_disk(local_abspath, props=None, skip_checks=False, notify=None)" },
1849     { "get_prop_diffs",
1850         (PyCFunction)py_wc_get_prop_diffs,
1851         METH_VARARGS|METH_KEYWORDS,
1852         "get_prop_diffs(path) -> (changes orig_props)" },
1853     { NULL }
1854 };
1855
1856 static void context_dealloc(PyObject *self)
1857 {
1858     ContextObject *context_obj = (ContextObject *)self;
1859     svn_wc_context_destroy(context_obj->context);
1860     apr_pool_destroy(context_obj->pool);
1861     PyObject_Del(self);
1862 }
1863
1864 static PyObject *context_init(PyTypeObject *self, PyObject *args, PyObject *kwargs)
1865 {
1866     ContextObject *ret;
1867     char *kwnames[] = { NULL };
1868     svn_config_t *config = NULL;
1869
1870     // TODO(jelmer): Support passing in config
1871     if (!PyArg_ParseTupleAndKeywords(args, kwargs, "", kwnames))
1872         return NULL;
1873
1874     ret = PyObject_New(ContextObject, &Context_Type);
1875     if (ret == NULL)
1876         return NULL;
1877
1878     ret->pool = Pool(NULL);
1879     if (ret->pool == NULL)
1880         return NULL;
1881     RUN_SVN_WITH_POOL(ret->pool, svn_wc_context_create(&ret->context, config,
1882                                                        ret->pool, ret->pool));
1883
1884     return (PyObject *)ret;
1885 }
1886
1887 static PyTypeObject Context_Type = {
1888         PyVarObject_HEAD_INIT(NULL, 0)
1889         "wc.Context", /*        const char *tp_name;  For printing, in format "<module>.<name>" */
1890         sizeof(ContextObject),
1891         0,/*    Py_ssize_t tp_basicsize, tp_itemsize;  For allocation */
1892
1893         /* Methods to implement standard operations */
1894
1895         context_dealloc, /*     destructor tp_dealloc;  */
1896         NULL, /*        printfunc tp_print;     */
1897         NULL, /*        getattrfunc tp_getattr; */
1898         NULL, /*        setattrfunc tp_setattr; */
1899         NULL, /*        cmpfunc tp_compare;     */
1900         NULL, /*        reprfunc tp_repr;       */
1901
1902         /* Method suites for standard classes */
1903
1904         NULL, /*        PyNumberMethods *tp_as_number;  */
1905         NULL, /*        PySequenceMethods *tp_as_sequence;      */
1906         NULL, /*        PyMappingMethods *tp_as_mapping;        */
1907
1908         /* More standard operations (here for binary compatibility) */
1909
1910         NULL, /*        hashfunc tp_hash;       */
1911         NULL, /*        ternaryfunc tp_call;    */
1912         NULL, /*        reprfunc tp_str;        */
1913         NULL, /*        getattrofunc tp_getattro;       */
1914         NULL, /*        setattrofunc tp_setattro;       */
1915
1916         /* Functions to access object as input/output buffer */
1917         NULL, /*        PyBufferProcs *tp_as_buffer;    */
1918
1919         /* Flags to define presence of optional/expanded features */
1920         0, /*   long tp_flags;  */
1921
1922         "Context", /*   const char *tp_doc;  Documentation string */
1923
1924         /* Assigned meaning in release 2.0 */
1925         /* call function for all accessible objects */
1926         NULL, /*        traverseproc tp_traverse;       */
1927
1928         /* delete references to contained objects */
1929         NULL, /*        inquiry tp_clear;       */
1930
1931         /* Assigned meaning in release 2.1 */
1932         /* rich comparisons */
1933         NULL, /*        richcmpfunc tp_richcompare;     */
1934
1935         /* weak reference enabler */
1936         0, /*   Py_ssize_t tp_weaklistoffset;   */
1937
1938         /* Added in release 2.2 */
1939         /* Iterators */
1940         NULL, /*        getiterfunc tp_iter;    */
1941         NULL, /*        iternextfunc tp_iternext;       */
1942
1943         /* Attribute descriptor and subclassing stuff */
1944         context_methods, /*     struct PyMethodDef *tp_methods; */
1945         NULL, /*        struct PyMemberDef *tp_members; */
1946         NULL, /*        struct PyGetSetDef *tp_getset;  */
1947         NULL, /*        struct _typeobject *tp_base;    */
1948         NULL, /*        PyObject *tp_dict;      */
1949         NULL, /*        descrgetfunc tp_descr_get;      */
1950         NULL, /*        descrsetfunc tp_descr_set;      */
1951         0, /*   Py_ssize_t tp_dictoffset;       */
1952         NULL, /*        initproc tp_init;       */
1953         NULL, /*        allocfunc tp_alloc;     */
1954         context_init, /*        newfunc tp_new; */
1955 };
1956
1957 #endif
1958
1959 static void lock_dealloc(PyObject *self)
1960 {
1961         LockObject *lockself = (LockObject *)self;
1962
1963         apr_pool_destroy(lockself->pool);
1964
1965         PyObject_Del(self);
1966 }
1967
1968 static PyObject *lock_init(PyTypeObject *type, PyObject *args, PyObject *kwargs)
1969 {
1970         char *kwnames[] = { NULL };
1971         LockObject *ret;
1972
1973         if (!PyArg_ParseTupleAndKeywords(args, kwargs, "", kwnames))
1974                 return NULL;
1975
1976         ret = PyObject_New(LockObject, &Lock_Type);
1977         if (ret == NULL)
1978                 return NULL;
1979
1980         ret->pool = Pool(NULL);
1981         if (ret->pool == NULL)
1982                 return NULL;
1983         ret->lock = svn_lock_create(ret->pool);
1984
1985         return (PyObject *)ret;
1986 }
1987
1988 PyTypeObject Lock_Type = {
1989         PyVarObject_HEAD_INIT(NULL, 0)
1990         "wc.Lock", /*   const char *tp_name;  For printing, in format "<module>.<name>" */
1991         sizeof(LockObject),
1992         0,/*    Py_ssize_t tp_basicsize, tp_itemsize;  For allocation */
1993
1994         /* Methods to implement standard operations */
1995
1996         .tp_dealloc = lock_dealloc, /*  destructor tp_dealloc;  */
1997
1998         .tp_doc = "Lock", /*    const char *tp_doc;  Documentation string */
1999
2000         .tp_methods = NULL, /*  struct PyMethodDef *tp_methods; */
2001
2002         .tp_new = lock_init, /* tp_new tp_new */
2003 };
2004
2005 static PyObject *
2006 moduleinit(void)
2007 {
2008         PyObject *mod;
2009
2010         if (PyType_Ready(&Entry_Type) < 0)
2011                 return NULL;
2012
2013         if (PyType_Ready(&Status2_Type) < 0)
2014                 return NULL;
2015
2016         if (PyType_Ready(&Adm_Type) < 0)
2017                 return NULL;
2018
2019 #if ONLY_SINCE_SVN(1, 7)
2020         if (PyType_Ready(&Context_Type) < 0)
2021                 return NULL;
2022 #endif
2023
2024         if (PyType_Ready(&Editor_Type) < 0)
2025                 return NULL;
2026
2027         if (PyType_Ready(&FileEditor_Type) < 0)
2028                 return NULL;
2029
2030         if (PyType_Ready(&DirectoryEditor_Type) < 0)
2031                 return NULL;
2032
2033         if (PyType_Ready(&TxDeltaWindowHandler_Type) < 0)
2034                 return NULL;
2035
2036         if (PyType_Ready(&Stream_Type) < 0)
2037                 return NULL;
2038
2039         if (PyType_Ready(&CommittedQueue_Type) < 0)
2040                 return NULL;
2041
2042 #if ONLY_SINCE_SVN(1, 7)
2043         if (PyType_Ready(&Status3_Type) < 0)
2044                 return NULL;
2045 #endif
2046
2047         if (PyType_Ready(&Lock_Type) < 0)
2048                 return NULL;
2049
2050         apr_initialize();
2051
2052 #if PY_MAJOR_VERSION >= 3
2053         static struct PyModuleDef moduledef = {
2054           PyModuleDef_HEAD_INIT,
2055           "wc",         /* m_name */
2056           "Working Copies", /* m_doc */
2057           -1,              /* m_size */
2058           wc_methods, /* m_methods */
2059           NULL,            /* m_reload */
2060           NULL,            /* m_traverse */
2061           NULL,            /* m_clear*/
2062           NULL,            /* m_free */
2063         };
2064         mod = PyModule_Create(&moduledef);
2065 #else
2066         mod = Py_InitModule3("wc", wc_methods, "Working Copies");
2067 #endif
2068         if (mod == NULL)
2069                 return NULL;
2070
2071         PyModule_AddIntConstant(mod, "SCHEDULE_NORMAL", 0);
2072         PyModule_AddIntConstant(mod, "SCHEDULE_ADD", 1);
2073         PyModule_AddIntConstant(mod, "SCHEDULE_DELETE", 2);
2074         PyModule_AddIntConstant(mod, "SCHEDULE_REPLACE", 3);
2075
2076 #if ONLY_SINCE_SVN(1, 5)
2077         PyModule_AddIntConstant(mod, "CONFLICT_CHOOSE_POSTPONE",
2078                                                         svn_wc_conflict_choose_postpone);
2079         PyModule_AddIntConstant(mod, "CONFLICT_CHOOSE_BASE",
2080                                                         svn_wc_conflict_choose_base);
2081         PyModule_AddIntConstant(mod, "CONFLICT_CHOOSE_THEIRS_FULL",
2082                                                         svn_wc_conflict_choose_theirs_full);
2083         PyModule_AddIntConstant(mod, "CONFLICT_CHOOSE_MINE_FULL",
2084                                                         svn_wc_conflict_choose_mine_full);
2085         PyModule_AddIntConstant(mod, "CONFLICT_CHOOSE_THEIRS_CONFLICT",
2086                                                         svn_wc_conflict_choose_theirs_conflict);
2087         PyModule_AddIntConstant(mod, "CONFLICT_CHOOSE_MINE_CONFLICT",
2088                                                         svn_wc_conflict_choose_mine_conflict);
2089         PyModule_AddIntConstant(mod, "CONFLICT_CHOOSE_MERGED",
2090                                                         svn_wc_conflict_choose_merged);
2091 #endif
2092
2093         PyModule_AddIntConstant(mod, "STATUS_NONE", svn_wc_status_none);
2094         PyModule_AddIntConstant(mod, "STATUS_UNVERSIONED", svn_wc_status_unversioned);
2095         PyModule_AddIntConstant(mod, "STATUS_NORMAL", svn_wc_status_normal);
2096         PyModule_AddIntConstant(mod, "STATUS_ADDED", svn_wc_status_added);
2097         PyModule_AddIntConstant(mod, "STATUS_MISSING", svn_wc_status_missing);
2098         PyModule_AddIntConstant(mod, "STATUS_DELETED", svn_wc_status_deleted);
2099         PyModule_AddIntConstant(mod, "STATUS_REPLACED", svn_wc_status_replaced);
2100         PyModule_AddIntConstant(mod, "STATUS_MODIFIED", svn_wc_status_modified);
2101         PyModule_AddIntConstant(mod, "STATUS_MERGED", svn_wc_status_merged);
2102         PyModule_AddIntConstant(mod, "STATUS_CONFLICTED", svn_wc_status_conflicted);
2103         PyModule_AddIntConstant(mod, "STATUS_IGNORED", svn_wc_status_ignored);
2104         PyModule_AddIntConstant(mod, "STATUS_OBSTRUCTED", svn_wc_status_obstructed);
2105         PyModule_AddIntConstant(mod, "STATUS_EXTERNAL", svn_wc_status_external);
2106         PyModule_AddIntConstant(mod, "STATUS_INCOMPLETE", svn_wc_status_incomplete);
2107
2108         PyModule_AddIntConstant(mod, "TRANSLATE_FROM_NF", SVN_WC_TRANSLATE_FROM_NF);
2109         PyModule_AddIntConstant(mod, "TRANSLATE_TO_NF", SVN_WC_TRANSLATE_TO_NF);
2110         PyModule_AddIntConstant(mod, "TRANSLATE_FORCE_EOL_REPAIR", SVN_WC_TRANSLATE_FORCE_EOL_REPAIR);
2111         PyModule_AddIntConstant(mod, "TRANSLATE_NO_OUTPUT_CLEANUP", SVN_WC_TRANSLATE_NO_OUTPUT_CLEANUP);
2112         PyModule_AddIntConstant(mod, "TRANSLATE_FORCE_COPY", SVN_WC_TRANSLATE_FORCE_COPY);
2113         PyModule_AddIntConstant(mod, "TRANSLATE_USE_GLOBAL_TMP", SVN_WC_TRANSLATE_USE_GLOBAL_TMP);
2114
2115 #if ONLY_SINCE_SVN(1, 5)
2116         PyModule_AddIntConstant(mod, "CONFLICT_CHOOSE_POSTPONE", svn_wc_conflict_choose_postpone);
2117         PyModule_AddIntConstant(mod, "CONFLICT_CHOOSE_BASE", svn_wc_conflict_choose_base);
2118         PyModule_AddIntConstant(mod, "CONFLICT_CHOOSE_THEIRS_FULL", svn_wc_conflict_choose_theirs_full);
2119         PyModule_AddIntConstant(mod, "CONFLICT_CHOOSE_MINE_FULL", svn_wc_conflict_choose_mine_full);
2120         PyModule_AddIntConstant(mod, "CONFLICT_CHOOSE_THEIRS_CONFLICT", svn_wc_conflict_choose_theirs_conflict);
2121         PyModule_AddIntConstant(mod, "CONFLICT_CHOOSE_MINE_CONFLICT", svn_wc_conflict_choose_mine_conflict);
2122         PyModule_AddIntConstant(mod, "CONFLICT_CHOOSE_MERGED", svn_wc_conflict_choose_merged);
2123 #endif
2124
2125         PyModule_AddObject(mod, "Adm", (PyObject *)&Adm_Type);
2126         Py_INCREF(&Adm_Type);
2127
2128         PyModule_AddObject(mod, "Lock", (PyObject *)&Lock_Type);
2129         Py_INCREF(&Lock_Type);
2130
2131         PyModule_AddObject(mod, "CommittedQueue", (PyObject *)&CommittedQueue_Type);
2132         Py_INCREF(&CommittedQueue_Type);
2133
2134 #if ONLY_SINCE_SVN(1, 7)
2135         PyModule_AddObject(mod, "Context", (PyObject *)&Context_Type);
2136         Py_INCREF(&Context_Type);
2137 #endif
2138
2139         return mod;
2140 }
2141
2142 #if PY_MAJOR_VERSION >= 3
2143 PyMODINIT_FUNC
2144 PyInit_wc(void)
2145 {
2146         return moduleinit();
2147 }
2148 #else
2149 PyMODINIT_FUNC
2150 initwc(void)
2151 {
2152         moduleinit();
2153 }
2154 #endif