Merge trunk.
[jelmer/subvertpy.git] / subvertpy / wc.c
1 /*
2  * Copyright © 2008 Jelmer Vernooij <jelmer@samba.org>
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 General Public License as published by
7  * the Free Software Foundation; either version 2 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 General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  */
19 #include <Python.h>
20 #include <apr_general.h>
21 #include <svn_wc.h>
22 #include <svn_path.h>
23 #include <structmember.h>
24 #include <stdbool.h>
25
26 #include "util.h"
27 #include "editor.h"
28
29 extern PyTypeObject Entry_Type;
30 extern PyTypeObject Adm_Type;
31
32 static PyObject *py_entry(const svn_wc_entry_t *entry);
33
34 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)
35 {
36         PyObject *self = (PyObject *)baton, *py_lock_token, *ret;
37         PyGILState_STATE state = PyGILState_Ensure();
38         if (lock_token == NULL) {
39                 py_lock_token = Py_None;
40         } else {
41                 py_lock_token = PyString_FromString(lock_token);
42         }
43         ret = PyObject_CallMethod(self, "set_path", "slbO", path, revision, start_empty, py_lock_token);
44         CB_CHECK_PYRETVAL(ret);
45         PyGILState_Release(state);
46         return NULL;
47 }
48
49 static svn_error_t *py_ra_report_delete_path(void *baton, const char *path, apr_pool_t *pool)
50 {
51         PyObject *self = (PyObject *)baton, *ret;
52         PyGILState_STATE state = PyGILState_Ensure();
53         ret = PyObject_CallMethod(self, "delete_path", "s", path);
54         CB_CHECK_PYRETVAL(ret);
55         PyGILState_Release(state);
56         return NULL;
57 }
58
59 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)
60 {
61         PyObject *self = (PyObject *)report_baton, *ret, *py_lock_token;
62         PyGILState_STATE state = PyGILState_Ensure();
63         if (lock_token == NULL) {
64                 py_lock_token = Py_None;
65         } else { 
66                 py_lock_token = PyString_FromString(lock_token);
67         }
68         ret = PyObject_CallMethod(self, "link_path", "sslbO", path, url, revision, start_empty, py_lock_token);
69         CB_CHECK_PYRETVAL(ret);
70         PyGILState_Release(state);
71         return NULL;
72 }
73
74 static svn_error_t *py_ra_report_finish(void *baton, apr_pool_t *pool)
75 {
76         PyObject *self = (PyObject *)baton, *ret;
77         PyGILState_STATE state = PyGILState_Ensure();
78         ret = PyObject_CallMethod(self, "finish", "");
79         CB_CHECK_PYRETVAL(ret);
80         PyGILState_Release(state);
81         return NULL;
82 }
83
84 static svn_error_t *py_ra_report_abort(void *baton, apr_pool_t *pool)
85 {
86         PyObject *self = (PyObject *)baton, *ret;
87         PyGILState_STATE state = PyGILState_Ensure();
88         ret = PyObject_CallMethod(self, "abort", "");
89         CB_CHECK_PYRETVAL(ret);
90         PyGILState_Release(state);
91         return NULL;
92 }
93
94 static const svn_ra_reporter2_t py_ra_reporter = {
95         py_ra_report_set_path,
96         py_ra_report_delete_path,
97         py_ra_report_link_path,
98         py_ra_report_finish,
99         py_ra_report_abort,
100 };
101
102
103
104 /**
105  * Get libsvn_wc version information.
106  *
107  * :return: tuple with major, minor, patch version number and tag.
108  */
109 static PyObject *version(PyObject *self)
110 {
111         const svn_version_t *ver = svn_wc_version();
112         return Py_BuildValue("(iiis)", ver->major, ver->minor, 
113                                                  ver->patch, ver->tag);
114 }
115
116 static svn_error_t *py_wc_found_entry(const char *path, const svn_wc_entry_t *entry, void *walk_baton, apr_pool_t *pool)
117 {
118         PyObject *fn = (PyObject *)walk_baton, *ret;
119         PyGILState_STATE state = PyGILState_Ensure();
120         ret = PyObject_CallFunction(fn, "sO", path, py_entry(entry));
121         CB_CHECK_PYRETVAL(ret);
122         PyGILState_Release(state);
123         return NULL;
124 }
125
126 static svn_wc_entry_callbacks_t py_wc_entry_callbacks = {
127         py_wc_found_entry
128 };
129
130 void py_wc_notify_func(void *baton, const svn_wc_notify_t *notify, apr_pool_t *pool)
131 {
132         PyObject *func = baton, *ret;
133         if (func == Py_None)
134                 return;
135
136         if (notify->err != NULL) {
137                 ret = PyObject_CallFunction(func, "O", PyErr_NewSubversionException(notify->err));
138                 Py_XDECREF(ret);
139                 /* FIXME: Use return value */
140         }
141 }
142
143 typedef struct {
144         PyObject_HEAD
145         apr_pool_t *pool;
146         svn_wc_entry_t entry;
147 } EntryObject;
148
149 static void entry_dealloc(PyObject *self)
150 {
151         apr_pool_destroy(((EntryObject *)self)->pool);
152         PyObject_Del(self);
153 }
154
155 static PyMemberDef entry_members[] = {
156         { "name", T_STRING, offsetof(EntryObject, entry.name), READONLY, NULL },
157         { "copyfrom_url", T_STRING, offsetof(EntryObject, entry.copyfrom_url), READONLY, NULL },
158         { "copyfrom_rev", T_LONG, offsetof(EntryObject, entry.copyfrom_rev), READONLY, NULL },
159         { "url", T_STRING, offsetof(EntryObject, entry.url), READONLY, NULL },
160         { "repos", T_STRING, offsetof(EntryObject, entry.repos), READONLY, NULL },
161         { "schedule", T_INT, offsetof(EntryObject, entry.schedule), READONLY, NULL },
162         { "kind", T_INT, offsetof(EntryObject, entry.kind), READONLY, NULL },
163         { "revision", T_LONG, offsetof(EntryObject, entry.revision), READONLY, NULL },
164         { "cmt_rev", T_LONG, offsetof(EntryObject, entry.cmt_rev), READONLY, NULL },
165         { "checksum", T_STRING, offsetof(EntryObject, entry.checksum), READONLY, NULL },
166         { "cmt_date", T_LONG, offsetof(EntryObject, entry.cmt_date), READONLY, NULL },
167         { "cmt_author", T_STRING, offsetof(EntryObject, entry.cmt_author), READONLY, NULL },
168         { NULL, }
169 };
170
171 PyTypeObject Entry_Type = {
172         PyObject_HEAD_INIT(NULL) 0,
173         "wc.Entry", /*  const char *tp_name;  For printing, in format "<module>.<name>" */
174         sizeof(EntryObject), 
175         0,/*    Py_ssize_t tp_basicsize, tp_itemsize;  For allocation */
176         
177         /* Methods to implement standard operations */
178         
179         entry_dealloc, /*       destructor tp_dealloc;  */
180         NULL, /*        printfunc tp_print;     */
181         NULL, /*        getattrfunc tp_getattr; */
182         NULL, /*        setattrfunc tp_setattr; */
183         NULL, /*        cmpfunc tp_compare;     */
184         NULL, /*        reprfunc tp_repr;       */
185         
186         /* Method suites for standard classes */
187         
188         NULL, /*        PyNumberMethods *tp_as_number;  */
189         NULL, /*        PySequenceMethods *tp_as_sequence;      */
190         NULL, /*        PyMappingMethods *tp_as_mapping;        */
191         
192         /* More standard operations (here for binary compatibility) */
193         
194         NULL, /*        hashfunc tp_hash;       */
195         NULL, /*        ternaryfunc tp_call;    */
196         NULL, /*        reprfunc tp_str;        */
197         NULL, /*        getattrofunc tp_getattro;       */
198         NULL, /*        setattrofunc tp_setattro;       */
199         
200         /* Functions to access object as input/output buffer */
201         NULL, /*        PyBufferProcs *tp_as_buffer;    */
202         
203         /* Flags to define presence of optional/expanded features */
204         0, /*   long tp_flags;  */
205         
206         NULL, /*        const char *tp_doc;  Documentation string */
207         
208         /* Assigned meaning in release 2.0 */
209         /* call function for all accessible objects */
210         NULL, /*        traverseproc tp_traverse;       */
211         
212         /* delete references to contained objects */
213         NULL, /*        inquiry tp_clear;       */
214         
215         /* Assigned meaning in release 2.1 */
216         /* rich comparisons */
217         NULL, /*        richcmpfunc tp_richcompare;     */
218         
219         /* weak reference enabler */
220         0, /*   Py_ssize_t tp_weaklistoffset;   */
221         
222         /* Added in release 2.2 */
223         /* Iterators */
224         NULL, /*        getiterfunc tp_iter;    */
225         NULL, /*        iternextfunc tp_iternext;       */
226         
227         /* Attribute descriptor and subclassing stuff */
228         NULL, /*        struct PyMethodDef *tp_methods; */
229         entry_members, /*       struct PyMemberDef *tp_members; */
230
231 };
232
233 static PyObject *py_entry(const svn_wc_entry_t *entry)
234 {
235         EntryObject *ret = PyObject_New(EntryObject, &Entry_Type);
236         if (ret == NULL)
237                 return NULL;
238
239         ret->pool = Pool(NULL);
240         if (ret->pool == NULL)
241                 return NULL;
242         ret->entry = *svn_wc_entry_dup(entry, ret->pool);
243         return (PyObject *)ret;
244 }
245
246 typedef struct {
247         PyObject_HEAD
248         svn_wc_adm_access_t *adm;
249         apr_pool_t *pool;
250 } AdmObject;
251
252 static PyObject *adm_init(PyTypeObject *self, PyObject *args, PyObject *kwargs)
253 {
254         PyObject *associated;
255         char *path;
256         bool write_lock=false;
257         int depth=0;
258         PyObject *cancel_func=Py_None;
259         svn_wc_adm_access_t *parent_wc;
260         svn_error_t *err;
261         AdmObject *ret;
262         char *kwnames[] = { "associated", "path", "write_lock", "depth", "cancel_func", NULL };
263
264         if (!PyArg_ParseTupleAndKeywords(args, kwargs, "Os|biO", kwnames, &associated, &path, &write_lock, &depth, &cancel_func))
265                 return NULL;
266
267         ret = PyObject_New(AdmObject, &Adm_Type);
268         if (ret == NULL)
269                 return NULL;
270
271         ret->pool = Pool(NULL);
272         if (ret->pool == NULL)
273                 return NULL;
274         if (associated == Py_None) {
275                 parent_wc = NULL;
276         } else {
277                 parent_wc = ((AdmObject *)associated)->adm;
278         }
279         Py_BEGIN_ALLOW_THREADS
280         err = svn_wc_adm_open3(&ret->adm, parent_wc, path, 
281                                          write_lock, depth, py_cancel_func, cancel_func, 
282                                          ret->pool);
283         Py_END_ALLOW_THREADS
284         
285         if (!check_error(err)) {
286                 return NULL;
287         }
288
289         return (PyObject *)ret;
290 }
291
292 static PyObject *adm_access_path(PyObject *self)
293 {
294         AdmObject *admobj = (AdmObject *)self;
295         return PyString_FromString(svn_wc_adm_access_path(admobj->adm));
296 }
297
298 static PyObject *adm_locked(PyObject *self)
299 {
300         AdmObject *admobj = (AdmObject *)self;
301         return PyBool_FromLong(svn_wc_adm_locked(admobj->adm));
302 }
303
304 static PyObject *adm_prop_get(PyObject *self, PyObject *args)
305 {
306         char *name, *path;
307         AdmObject *admobj = (AdmObject *)self;
308         const svn_string_t *value;
309         apr_pool_t *temp_pool;
310         PyObject *ret;
311
312         if (!PyArg_ParseTuple(args, "ss", &name, &path))
313                 return NULL;
314
315         temp_pool = Pool(NULL);
316         if (temp_pool == NULL)
317                 return NULL;
318         RUN_SVN_WITH_POOL(temp_pool, svn_wc_prop_get(&value, name, path, admobj->adm, temp_pool));
319         if (value == NULL || value->data == NULL) {
320                 ret = Py_None;
321         } else {
322                 ret = PyString_FromStringAndSize(value->data, value->len);
323         }
324         apr_pool_destroy(temp_pool);
325         return ret;
326 }
327
328 static PyObject *adm_prop_set(PyObject *self, PyObject *args)
329 {
330         char *name, *value, *path; 
331         AdmObject *admobj = (AdmObject *)self;
332         bool skip_checks=false;
333         apr_pool_t *temp_pool;
334         int vallen;
335         svn_string_t *cvalue;
336
337         if (!PyArg_ParseTuple(args, "ss#s|b", &name, &value, &vallen, &path, &skip_checks))
338                 return NULL;
339
340         temp_pool = Pool(NULL);
341         if (temp_pool == NULL)
342                 return NULL;
343         cvalue = svn_string_ncreate(value, vallen, temp_pool);
344         RUN_SVN_WITH_POOL(temp_pool, svn_wc_prop_set2(name, cvalue, path, admobj->adm, 
345                                 skip_checks, temp_pool));
346         apr_pool_destroy(temp_pool);
347
348         Py_RETURN_NONE;
349 }
350
351 static PyObject *adm_entries_read(PyObject *self, PyObject *args)
352 {
353         apr_hash_t *entries;
354         AdmObject *admobj = (AdmObject *)self;
355         apr_pool_t *temp_pool;
356         bool show_hidden=false;
357         apr_hash_index_t *idx;
358         const char *key;
359         apr_ssize_t klen;
360         svn_wc_entry_t *entry;
361         PyObject *py_entries;
362
363         if (!PyArg_ParseTuple(args, "|b", &show_hidden))
364                 return NULL;
365
366         temp_pool = Pool(NULL);
367         if (temp_pool == NULL)
368                 return NULL;
369         RUN_SVN_WITH_POOL(temp_pool, svn_wc_entries_read(&entries, admobj->adm, 
370                                  show_hidden, temp_pool));
371         py_entries = PyDict_New();
372         idx = apr_hash_first(temp_pool, entries);
373         while (idx != NULL) {
374                 apr_hash_this(idx, (const void **)&key, &klen, (void **)&entry);
375                 PyDict_SetItemString(py_entries, key, py_entry(entry));
376                 idx = apr_hash_next(idx);
377         }
378         apr_pool_destroy(temp_pool);
379         return py_entries;
380 }
381
382 static PyObject *adm_walk_entries(PyObject *self, PyObject *args)
383 {
384         char *path;
385         PyObject *callbacks; 
386         bool show_hidden=false;
387         PyObject *cancel_func=Py_None;
388         apr_pool_t *temp_pool;
389         AdmObject *admobj = (AdmObject *)self;
390
391         if (!PyArg_ParseTuple(args, "sO|bO", &path, &callbacks, &show_hidden, &cancel_func))
392                 return NULL;
393
394         temp_pool = Pool(NULL);
395         if (temp_pool == NULL)
396                 return NULL;
397         RUN_SVN_WITH_POOL(temp_pool, svn_wc_walk_entries2(path, admobj->adm, 
398                                 &py_wc_entry_callbacks, (void *)callbacks,
399                                 show_hidden, py_cancel_func, (void *)cancel_func,
400                                 temp_pool));
401         apr_pool_destroy(temp_pool);
402
403         Py_RETURN_NONE;
404 }
405
406 static PyObject *adm_entry(PyObject *self, PyObject *args)
407 {
408         char *path;
409         bool show_hidden=false;
410         apr_pool_t *temp_pool;
411         AdmObject *admobj = (AdmObject *)self;
412         const svn_wc_entry_t *entry;
413
414         if (!PyArg_ParseTuple(args, "s|b", &path, &show_hidden))
415                 return NULL;
416
417         temp_pool = Pool(NULL);
418         if (temp_pool == NULL)
419                 return NULL;
420         RUN_SVN_WITH_POOL(temp_pool, svn_wc_entry(&entry, path, admobj->adm, show_hidden, temp_pool));
421         apr_pool_destroy(temp_pool);
422
423         return py_entry(entry);
424 }
425
426 static PyObject *adm_get_prop_diffs(PyObject *self, PyObject *args)
427 {
428         char *path;
429         apr_pool_t *temp_pool;
430         apr_array_header_t *propchanges;
431         apr_hash_t *original_props;
432         AdmObject *admobj = (AdmObject *)self;
433         svn_prop_t el;
434         int i;
435         PyObject *py_propchanges, *py_orig_props, *pyval;
436
437         if (!PyArg_ParseTuple(args, "s", &path))
438                 return NULL;
439
440         temp_pool = Pool(NULL);
441         if (temp_pool == NULL)
442                 return NULL;
443         RUN_SVN_WITH_POOL(temp_pool, svn_wc_get_prop_diffs(&propchanges, &original_props, 
444                                 svn_path_canonicalize(path, temp_pool), admobj->adm, temp_pool));
445         py_propchanges = PyList_New(propchanges->nelts);
446         for (i = 0; i < propchanges->nelts; i++) {
447                 el = APR_ARRAY_IDX(propchanges, i, svn_prop_t);
448                 pyval = Py_BuildValue("(ss#)", el.name, el.value->data, el.value->len);
449                 if (pyval == NULL) {
450                         apr_pool_destroy(temp_pool);
451                         return NULL;
452                 }
453                 PyList_SetItem(py_propchanges, i, pyval);
454         }
455         py_orig_props = prop_hash_to_dict(original_props);
456         apr_pool_destroy(temp_pool);
457         if (py_orig_props == NULL)
458                 return NULL;
459         return Py_BuildValue("(NN)", py_propchanges, py_orig_props);
460 }
461
462 static PyObject *adm_add(PyObject *self, PyObject *args)
463 {
464         char *path, *copyfrom_url=NULL;
465         svn_revnum_t copyfrom_rev=-1; 
466         PyObject *cancel_func=Py_None, *notify_func=Py_None;
467         AdmObject *admobj = (AdmObject *)self;
468         apr_pool_t *temp_pool;
469
470         temp_pool = Pool(NULL);
471         if (temp_pool == NULL)
472                 return NULL;
473
474         if (!PyArg_ParseTuple(args, "s|zlOO", &path, &copyfrom_url, &copyfrom_rev, &cancel_func, &notify_func))
475                 return NULL;
476
477         RUN_SVN_WITH_POOL(temp_pool, svn_wc_add2(path, admobj->adm, copyfrom_url, 
478                                                         copyfrom_rev, py_cancel_func, 
479                                                         (void *)cancel_func,
480                                                         py_wc_notify_func, 
481                                                         (void *)notify_func, 
482                                                         temp_pool));
483         apr_pool_destroy(temp_pool);
484
485         Py_RETURN_NONE;
486 }
487
488 static PyObject *adm_copy(PyObject *self, PyObject *args)
489 {
490         AdmObject *admobj = (AdmObject *)self;
491         char *src, *dst; 
492         PyObject *cancel_func=Py_None, *notify_func=Py_None;
493         apr_pool_t *temp_pool;
494
495         if (!PyArg_ParseTuple(args, "ss|OO", &src, &dst, &cancel_func, &notify_func))
496                 return NULL;
497
498         temp_pool = Pool(NULL);
499         if (temp_pool == NULL)
500                 return NULL;
501         RUN_SVN_WITH_POOL(temp_pool, svn_wc_copy2(src, admobj->adm, dst,
502                                                         py_cancel_func, (void *)cancel_func,
503                                                         py_wc_notify_func, (void *)notify_func, 
504                                                         temp_pool));
505         apr_pool_destroy(temp_pool);
506
507         Py_RETURN_NONE;
508 }
509
510 static PyObject *adm_delete(PyObject *self, PyObject *args)
511 {
512         AdmObject *admobj = (AdmObject *)self;
513         apr_pool_t *temp_pool;
514         char *path;
515         PyObject *cancel_func=Py_None, *notify_func=Py_None;
516
517         if (!PyArg_ParseTuple(args, "s|OO", &path, &cancel_func, &notify_func))
518                 return NULL;
519
520         temp_pool = Pool(NULL);
521         if (temp_pool == NULL)
522                 return NULL;
523         RUN_SVN_WITH_POOL(temp_pool, svn_wc_delete2(path, admobj->adm, 
524                                                         py_cancel_func, (void *)cancel_func,
525                                                         py_wc_notify_func, (void *)notify_func, 
526                                                         temp_pool));
527         apr_pool_destroy(temp_pool);
528
529         Py_RETURN_NONE;
530 }
531
532 static PyObject *adm_crawl_revisions(PyObject *self, PyObject *args, PyObject *kwargs)
533 {
534         char *path;
535         PyObject *reporter;
536         bool restore_files=true, recurse=true, use_commit_times=true;
537         PyObject *notify_func=Py_None;
538         apr_pool_t *temp_pool;
539         AdmObject *admobj = (AdmObject *)self;
540         svn_wc_traversal_info_t *traversal_info;
541         char *kwnames[] = { "path", "reporter", "restore_files", "recurse", "use_commit_times", "notify_func", NULL };
542
543         if (!PyArg_ParseTupleAndKeywords(args, kwargs, "sO|bbbO", kwnames, &path, &reporter, &restore_files, &recurse, &use_commit_times,
544                                                   &notify_func))
545                 return NULL;
546
547         temp_pool = Pool(NULL);
548         if (temp_pool == NULL)
549                 return NULL;
550         traversal_info = svn_wc_init_traversal_info(temp_pool);
551         RUN_SVN_WITH_POOL(temp_pool, svn_wc_crawl_revisions2(path, admobj->adm, 
552                                 &py_ra_reporter, (void *)reporter, 
553                                 restore_files, recurse, use_commit_times, 
554                                 py_wc_notify_func, (void *)notify_func,
555                                 traversal_info, temp_pool));
556         apr_pool_destroy(temp_pool);
557
558         Py_RETURN_NONE;
559 }
560
561 static PyObject *adm_get_update_editor(PyObject *self, PyObject *args)
562 {
563         char *target;
564         bool use_commit_times=true, recurse=true;
565         PyObject * notify_func=Py_None, *cancel_func=Py_None;
566         char *diff3_cmd=NULL;
567         const svn_delta_editor_t *editor;
568         AdmObject *admobj = (AdmObject *)self;
569         void *edit_baton;
570         apr_pool_t *pool;
571         svn_revnum_t *latest_revnum;
572
573         if (!PyArg_ParseTuple(args, "s|bbOOz", &target, &use_commit_times, &recurse, &notify_func, &cancel_func, &diff3_cmd))
574                 return NULL;
575
576         pool = Pool(NULL);
577         if (pool == NULL)
578                 return NULL;
579         latest_revnum = (svn_revnum_t *)apr_palloc(pool, sizeof(svn_revnum_t));
580         Py_BEGIN_ALLOW_THREADS
581         if (!check_error(svn_wc_get_update_editor2(latest_revnum, admobj->adm, target, 
582                                 use_commit_times, recurse, py_wc_notify_func, (void *)notify_func, 
583                                 py_cancel_func, (void *)cancel_func, diff3_cmd, &editor, &edit_baton, 
584                                 NULL, pool))) {
585                 apr_pool_destroy(pool);
586                 PyEval_RestoreThread(_save);
587                 return NULL;
588         }
589         Py_END_ALLOW_THREADS
590         return new_editor_object(editor, edit_baton, pool, &Editor_Type, NULL, NULL);
591 }
592
593 static bool py_dict_to_wcprop_changes(PyObject *dict, apr_pool_t *pool, apr_array_header_t **ret)
594 {
595         PyObject *key, *val;
596         Py_ssize_t idx;
597
598         if (dict == Py_None) {
599                 *ret = NULL;
600                 return true;
601         }
602
603         if (!PyDict_Check(dict)) {
604                 PyErr_SetString(PyExc_TypeError, "Expected dictionary with property changes");
605                 return false;
606         }
607
608         *ret = apr_array_make(pool, PyDict_Size(dict), sizeof(char *));
609
610         while (PyDict_Next(dict, &idx, &key, &val)) {
611                    svn_prop_t *prop = apr_palloc(pool, sizeof(svn_prop_t));
612                    prop->name = PyString_AsString(key);
613                    if (val == Py_None) {
614                            prop->value = NULL;
615                    } else {
616                            prop->value = svn_string_ncreate(PyString_AsString(val), PyString_Size(val), pool);
617                    }
618                    APR_ARRAY_PUSH(*ret, svn_prop_t *) = prop;
619         }
620
621         return true;
622 }
623
624 static PyObject *adm_process_committed(PyObject *self, PyObject *args, PyObject *kwargs)
625 {
626         char *path, *rev_date, *rev_author;
627         bool recurse, remove_lock = false;
628         unsigned char *digest = NULL;
629         svn_revnum_t new_revnum;
630         PyObject *py_wcprop_changes = Py_None;
631         apr_array_header_t *wcprop_changes;
632         AdmObject *admobj = (AdmObject *)self;
633         apr_pool_t *temp_pool;
634         char *kwnames[] = { "path", "recurse", "new_revnum", "rev_date", "rev_author", 
635                                                 "wcprop_changes", "remove_lock", "digest", NULL };
636
637         if (!PyArg_ParseTupleAndKeywords(args, kwargs, "sblss|Obs", kwnames, 
638                                                                          &path, &recurse, &new_revnum, &rev_date,
639                                                                          &rev_author, &py_wcprop_changes, 
640                                                                          &remove_lock, &digest))
641                 return NULL;
642
643         temp_pool = Pool(NULL);
644         if (temp_pool == NULL)
645                 return NULL;
646
647         if (!py_dict_to_wcprop_changes(py_wcprop_changes, temp_pool, &wcprop_changes)) {
648                 apr_pool_destroy(temp_pool);
649                 return NULL;
650         }
651
652         RUN_SVN_WITH_POOL(temp_pool, svn_wc_process_committed3(path, admobj->adm, recurse, new_revnum, 
653                                                                                                                    rev_date, rev_author, wcprop_changes, 
654                                                                                                                    remove_lock, digest, temp_pool));
655
656         apr_pool_destroy(temp_pool);
657
658         return Py_None;
659 }
660
661 static PyObject *adm_close(PyObject *self)
662 {
663         AdmObject *admobj = (AdmObject *)self;
664         if (admobj->adm != NULL) {
665                 Py_BEGIN_ALLOW_THREADS
666                 svn_wc_adm_close(admobj->adm);
667                 Py_END_ALLOW_THREADS
668                 admobj->adm = NULL;
669         }
670
671         Py_RETURN_NONE;
672 }
673
674 static void adm_dealloc(PyObject *self)
675 {
676         apr_pool_destroy(((AdmObject *)self)->pool);
677         PyObject_Del(self);
678 }
679
680 static PyMethodDef adm_methods[] = { 
681         { "prop_set", adm_prop_set, METH_VARARGS, NULL },
682         { "access_path", (PyCFunction)adm_access_path, METH_NOARGS, NULL },
683         { "prop_get", adm_prop_get, METH_VARARGS, NULL },
684         { "entries_read", adm_entries_read, METH_VARARGS, NULL },
685         { "walk_entries", adm_walk_entries, METH_VARARGS, NULL },
686         { "locked", (PyCFunction)adm_locked, METH_NOARGS, NULL },
687         { "get_prop_diffs", adm_get_prop_diffs, METH_VARARGS, NULL },
688         { "add", adm_add, METH_VARARGS, NULL },
689         { "copy", adm_copy, METH_VARARGS, NULL },
690         { "delete", adm_delete, METH_VARARGS, NULL },
691         { "crawl_revisions", (PyCFunction)adm_crawl_revisions, METH_VARARGS|METH_KEYWORDS, NULL },
692         { "get_update_editor", adm_get_update_editor, METH_VARARGS, NULL },
693         { "close", (PyCFunction)adm_close, METH_NOARGS, NULL },
694         { "entry", (PyCFunction)adm_entry, METH_VARARGS, NULL },
695         { "process_committed", (PyCFunction)adm_process_committed, METH_VARARGS|METH_KEYWORDS, NULL },
696         { NULL, }
697 };
698
699 PyTypeObject Adm_Type = {
700         PyObject_HEAD_INIT(NULL) 0,
701         "wc.WorkingCopy", /*    const char *tp_name;  For printing, in format "<module>.<name>" */
702         sizeof(AdmObject), 
703         0,/*    Py_ssize_t tp_basicsize, tp_itemsize;  For allocation */
704         
705         /* Methods to implement standard operations */
706         
707         adm_dealloc, /* destructor tp_dealloc;  */
708         NULL, /*        printfunc tp_print;     */
709         NULL, /*        getattrfunc tp_getattr; */
710         NULL, /*        setattrfunc tp_setattr; */
711         NULL, /*        cmpfunc tp_compare;     */
712         NULL, /*        reprfunc tp_repr;       */
713         
714         /* Method suites for standard classes */
715         
716         NULL, /*        PyNumberMethods *tp_as_number;  */
717         NULL, /*        PySequenceMethods *tp_as_sequence;      */
718         NULL, /*        PyMappingMethods *tp_as_mapping;        */
719         
720         /* More standard operations (here for binary compatibility) */
721         
722         NULL, /*        hashfunc tp_hash;       */
723         NULL, /*        ternaryfunc tp_call;    */
724         NULL, /*        reprfunc tp_str;        */
725         NULL, /*        getattrofunc tp_getattro;       */
726         NULL, /*        setattrofunc tp_setattro;       */
727         
728         /* Functions to access object as input/output buffer */
729         NULL, /*        PyBufferProcs *tp_as_buffer;    */
730         
731         /* Flags to define presence of optional/expanded features */
732         0, /*   long tp_flags;  */
733         
734         NULL, /*        const char *tp_doc;  Documentation string */
735         
736         /* Assigned meaning in release 2.0 */
737         /* call function for all accessible objects */
738         NULL, /*        traverseproc tp_traverse;       */
739         
740         /* delete references to contained objects */
741         NULL, /*        inquiry tp_clear;       */
742         
743         /* Assigned meaning in release 2.1 */
744         /* rich comparisons */
745         NULL, /*        richcmpfunc tp_richcompare;     */
746         
747         /* weak reference enabler */
748         0, /*   Py_ssize_t tp_weaklistoffset;   */
749         
750         /* Added in release 2.2 */
751         /* Iterators */
752         NULL, /*        getiterfunc tp_iter;    */
753         NULL, /*        iternextfunc tp_iternext;       */
754         
755         /* Attribute descriptor and subclassing stuff */
756         adm_methods, /* struct PyMethodDef *tp_methods; */
757         NULL, /*        struct PyMemberDef *tp_members; */
758         NULL, /*        struct PyGetSetDef *tp_getset;  */
759         NULL, /*        struct _typeobject *tp_base;    */
760         NULL, /*        PyObject *tp_dict;      */
761         NULL, /*        descrgetfunc tp_descr_get;      */
762         NULL, /*        descrsetfunc tp_descr_set;      */
763         0, /*   Py_ssize_t tp_dictoffset;       */
764         NULL, /*        initproc tp_init;       */
765         NULL, /*        allocfunc tp_alloc;     */
766         adm_init, /*    newfunc tp_new; */
767
768 };
769
770 /** 
771  * Determine the revision status of a specified working copy.
772  *
773  * :return: Tuple with minimum and maximum revnums found, whether the 
774  * working copy was switched and whether it was modified.
775  */
776 static PyObject *revision_status(PyObject *self, PyObject *args, PyObject *kwargs)
777 {
778         char *kwnames[] = { "wc_path", "trail_url", "committed", "cancel_func", NULL };
779         char *wc_path, *trail_url=NULL;
780         bool committed=false;
781         PyObject *cancel_func=Py_None, *ret;
782          svn_wc_revision_status_t *revstatus;
783         apr_pool_t *temp_pool;
784
785         if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|zbO", kwnames, &wc_path, &trail_url, &committed, 
786                                                   &cancel_func))
787                 return NULL;
788
789         temp_pool = Pool(NULL);
790         if (temp_pool == NULL)
791                 return NULL;
792         RUN_SVN_WITH_POOL(temp_pool, svn_wc_revision_status(&revstatus, wc_path, trail_url,
793                                  committed, py_cancel_func, cancel_func, temp_pool));
794         ret = Py_BuildValue("(llbb)", revstatus->min_rev, revstatus->max_rev, 
795                         revstatus->switched, revstatus->modified);
796         apr_pool_destroy(temp_pool);
797         return ret;
798 }
799
800 static PyObject *is_normal_prop(PyObject *self, PyObject *args)
801 {
802         char *name;
803
804         if (!PyArg_ParseTuple(args, "s", &name))
805                 return NULL;
806
807         return PyBool_FromLong(svn_wc_is_normal_prop(name));
808 }
809
810 static PyObject *is_adm_dir(PyObject *self, PyObject *args)
811 {
812         char *name;
813         apr_pool_t *pool;
814         svn_boolean_t ret;
815
816         if (!PyArg_ParseTuple(args, "s", &name))
817                 return NULL;
818
819         pool = Pool(NULL);
820         if (pool == NULL)
821                 return NULL;
822
823         ret = svn_wc_is_adm_dir(name, pool);
824
825         apr_pool_destroy(pool);
826
827         return PyBool_FromLong(ret);
828 }
829
830 static PyObject *is_wc_prop(PyObject *self, PyObject *args)
831 {
832         char *name;
833
834         if (!PyArg_ParseTuple(args, "s", &name))
835                 return NULL;
836
837         return PyBool_FromLong(svn_wc_is_wc_prop(name));
838 }
839
840 static PyObject *is_entry_prop(PyObject *self, PyObject *args)
841 {
842         char *name;
843
844         if (!PyArg_ParseTuple(args, "s", &name))
845                 return NULL;
846
847         return PyBool_FromLong(svn_wc_is_entry_prop(name));
848 }
849
850 static PyObject *get_adm_dir(PyObject *self)
851 {
852         apr_pool_t *pool;
853         PyObject *ret;
854         const char *dir;
855         pool = Pool(NULL);
856         if (pool == NULL)
857                 return NULL;
858         dir = svn_wc_get_adm_dir(pool);
859         ret = PyString_FromString(dir);
860         apr_pool_destroy(pool);
861         return ret;
862 }
863
864 static PyObject *get_pristine_copy_path(PyObject *self, PyObject *args)
865 {
866         apr_pool_t *pool;
867         const char *pristine_path;
868         char *path;
869         PyObject *ret;
870
871         if (!PyArg_ParseTuple(args, "s", &path))
872                 return NULL;
873
874         pool = Pool(NULL);
875         if (pool == NULL)
876                 return NULL;
877         RUN_SVN_WITH_POOL(pool, svn_wc_get_pristine_copy_path(path, &pristine_path, pool));
878         ret = PyString_FromString(pristine_path);
879         apr_pool_destroy(pool);
880         return ret;
881 }
882
883 static PyObject *ensure_adm(PyObject *self, PyObject *args, PyObject *kwargs)
884 {
885         char *path, *uuid, *url;
886         char *repos=NULL; 
887         svn_revnum_t rev=-1;
888         apr_pool_t *pool;
889         char *kwnames[] = { "path", "uuid", "url", "repos", "rev", NULL };
890
891         if (!PyArg_ParseTupleAndKeywords(args, kwargs, "sss|sl", kwnames, 
892                                                                          &path, &uuid, &url, &repos, &rev))
893                 return NULL;
894
895         pool = Pool(NULL);
896         if (pool == NULL)
897                 return NULL;
898         RUN_SVN_WITH_POOL(pool, 
899                                           svn_wc_ensure_adm2(path, uuid, url, repos, rev, pool));
900         apr_pool_destroy(pool);
901         Py_RETURN_NONE;
902 }
903
904 static PyObject *check_wc(PyObject *self, PyObject *args)
905 {
906         char *path;
907         apr_pool_t *pool;
908         int wc_format;
909
910         if (!PyArg_ParseTuple(args, "s", &path))
911                 return NULL;
912
913         pool = Pool(NULL);
914         if (pool == NULL)
915                 return NULL;
916         RUN_SVN_WITH_POOL(pool, svn_wc_check_wc(path, &wc_format, pool));
917         apr_pool_destroy(pool);
918         return PyLong_FromLong(wc_format);
919 }
920
921 static PyMethodDef wc_methods[] = {
922         { "check_wc", check_wc, METH_VARARGS, NULL },
923         { "ensure_adm", (PyCFunction)ensure_adm, METH_KEYWORDS|METH_VARARGS, NULL },
924         { "get_adm_dir", (PyCFunction)get_adm_dir, METH_NOARGS, NULL },
925         { "get_pristine_copy_path", get_pristine_copy_path, METH_VARARGS, NULL },
926         { "is_adm_dir", is_adm_dir, METH_VARARGS, NULL },
927         { "is_normal_prop", is_normal_prop, METH_VARARGS, NULL },
928         { "is_entry_prop", is_entry_prop, METH_VARARGS, NULL },
929         { "is_wc_prop", is_wc_prop, METH_VARARGS, NULL },
930         { "revision_status", (PyCFunction)revision_status, METH_KEYWORDS|METH_VARARGS, NULL },
931         { "version", (PyCFunction)version, METH_NOARGS, NULL },
932         { NULL, }
933 };
934
935 void initwc(void)
936 {
937         PyObject *mod;
938
939         if (PyType_Ready(&Entry_Type) < 0)
940                 return;
941
942         if (PyType_Ready(&Adm_Type) < 0)
943                 return;
944
945         if (PyType_Ready(&Editor_Type) < 0)
946                 return;
947
948         if (PyType_Ready(&FileEditor_Type) < 0)
949                 return;
950
951         if (PyType_Ready(&DirectoryEditor_Type) < 0)
952                 return;
953
954         if (PyType_Ready(&TxDeltaWindowHandler_Type) < 0)
955                 return;
956
957         apr_initialize();
958
959         mod = Py_InitModule3("wc", wc_methods, "Working Copies");
960         if (mod == NULL)
961                 return;
962
963         PyModule_AddIntConstant(mod, "SCHEDULE_NORMAL", 0);
964         PyModule_AddIntConstant(mod, "SCHEDULE_ADD", 1);
965         PyModule_AddIntConstant(mod, "SCHEDULE_DELETE", 2);
966         PyModule_AddIntConstant(mod, "SCHEDULE_REPLACE", 3);
967
968         PyModule_AddObject(mod, "WorkingCopy", (PyObject *)&Adm_Type);
969         Py_INCREF(&Adm_Type);
970 }