Consistently use absolute paths.
[jelmer/subvertpy.git] / subvertpy / wc_adm.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
29 #include "util.h"
30 #include "editor.h"
31 #include "wc.h"
32
33 /* Suppress warnings for this specific file, as it
34  * provides backwards compatibility with svn < 1.7
35  */
36 #pragma GCC diagnostic push
37 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
38
39 static svn_wc_entry_callbacks2_t py_wc_entry_callbacks2;
40 static PyObject *py_entry(const svn_wc_entry_t *entry);
41
42 typedef struct {
43     PyObject_VAR_HEAD
44     svn_wc_adm_access_t *adm;
45     apr_pool_t *pool;
46 } AdmObject;
47
48 typedef struct {
49         PyObject_VAR_HEAD
50         apr_pool_t *pool;
51         svn_wc_entry_t entry;
52 } EntryObject;
53
54 svn_wc_adm_access_t *Adm_GetAdmAccess(PyObject *obj) {
55     AdmObject *adm_obj = (AdmObject *)obj;
56     return adm_obj->adm;
57 }
58
59 #define ADM_CHECK_CLOSED(adm_obj) \
60     if (adm_obj->adm == NULL) { \
61         PyErr_SetString(PyExc_RuntimeError, "WorkingCopy instance already closed"); \
62         return NULL; \
63     }
64
65 svn_wc_adm_access_t *PyObject_GetAdmAccess(PyObject *obj)
66 {
67     return ((AdmObject *)obj)->adm;
68 }
69
70 static PyObject *adm_init(PyTypeObject *self, PyObject *args, PyObject *kwargs)
71 {
72     PyObject *associated, *py_path;
73     const char *path;
74     bool write_lock=false;
75     int depth=0;
76     svn_wc_adm_access_t *parent_wc;
77     svn_error_t *err;
78     AdmObject *ret;
79     char *kwnames[] = { "associated", "path", "write_lock", "depth", NULL };
80
81     if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OO|bi", kwnames,
82                                      &associated, &py_path, &write_lock, &depth))
83         return NULL;
84
85     ret = PyObject_New(AdmObject, &Adm_Type);
86     if (ret == NULL)
87         return NULL;
88
89     ret->pool = Pool(NULL);
90     if (ret->pool == NULL) {
91         return NULL;
92     }
93     if (associated == Py_None) {
94         parent_wc = NULL;
95     } else {
96         parent_wc = ((AdmObject *)associated)->adm;
97     }
98
99     path = py_object_to_svn_abspath(py_path, ret->pool);
100     if (path == NULL) {
101         Py_DECREF(ret);
102         return NULL;
103     }
104
105     Py_BEGIN_ALLOW_THREADS
106         err = svn_wc_adm_open3(&ret->adm, parent_wc,
107                                path,
108                                write_lock, depth, py_cancel_check, NULL,
109                                ret->pool);
110     Py_END_ALLOW_THREADS
111
112     if (err != NULL) {
113         handle_svn_error(err);
114         svn_error_clear(err);
115         Py_DECREF(ret);
116         return NULL;
117     }
118
119     return (PyObject *)ret;
120 }
121
122 static PyObject *adm_access_path(PyObject *self)
123 {
124     AdmObject *admobj = (AdmObject *)self;
125     ADM_CHECK_CLOSED(admobj);
126     return py_object_from_svn_abspath(svn_wc_adm_access_path(admobj->adm));
127 }
128
129 static PyObject *adm_locked(PyObject *self)
130 {
131     AdmObject *admobj = (AdmObject *)self;
132     ADM_CHECK_CLOSED(admobj);
133     return PyBool_FromLong(svn_wc_adm_locked(admobj->adm));
134 }
135
136 static PyObject *adm_prop_get(PyObject *self, PyObject *args)
137 {
138     char *name;
139     const char *path;
140     AdmObject *admobj = (AdmObject *)self;
141     const svn_string_t *value;
142     apr_pool_t *temp_pool;
143     PyObject *ret, *py_path;
144
145     if (!PyArg_ParseTuple(args, "sO", &name, &py_path))
146         return NULL;
147
148     ADM_CHECK_CLOSED(admobj);
149
150     temp_pool = Pool(NULL);
151     if (temp_pool == NULL) {
152         return NULL;
153     }
154
155     path = py_object_to_svn_abspath(py_path, temp_pool);
156     if (path == NULL) {
157         apr_pool_destroy(temp_pool);
158         return NULL;
159     }
160
161     RUN_SVN_WITH_POOL(temp_pool, svn_wc_prop_get(&value, name, path, admobj->adm, temp_pool));
162     if (value == NULL || value->data == NULL) {
163         ret = Py_None;
164         Py_INCREF(ret);
165     } else {
166         ret = PyBytes_FromStringAndSize(value->data, value->len);
167     }
168     apr_pool_destroy(temp_pool);
169     return ret;
170 }
171
172 static PyObject *adm_prop_set(PyObject *self, PyObject *args)
173 {
174     char *name, *value;
175     const char *path;
176     AdmObject *admobj = (AdmObject *)self;
177     bool skip_checks=false;
178     apr_pool_t *temp_pool;
179     int vallen;
180     svn_string_t *cvalue;
181     PyObject *py_path;
182     PyObject *notify_func = Py_None;
183
184     if (!PyArg_ParseTuple(args, "sz#O|bO", &name, &value, &vallen, &py_path, &skip_checks,
185                           &notify_func))
186         return NULL;
187
188     ADM_CHECK_CLOSED(admobj);
189
190     temp_pool = Pool(NULL);
191     if (temp_pool == NULL) {
192         return NULL;
193     }
194
195     path = py_object_to_svn_abspath(py_path, temp_pool);
196     if (path == NULL) {
197         apr_pool_destroy(temp_pool);
198         return NULL;
199     }
200
201     if (value == NULL) {
202         cvalue = NULL;
203     } else {
204         cvalue = svn_string_ncreate(value, vallen, temp_pool);
205     }
206 #if ONLY_SINCE_SVN(1, 6)
207     RUN_SVN_WITH_POOL(temp_pool, svn_wc_prop_set3(name, cvalue, path, admobj->adm,
208                                                   skip_checks, py_wc_notify_func, (void *)notify_func,
209                                                   temp_pool));
210 #else
211     RUN_SVN_WITH_POOL(temp_pool, svn_wc_prop_set2(name, cvalue, path, admobj->adm,
212                                                   skip_checks, temp_pool));
213 #endif
214     apr_pool_destroy(temp_pool);
215
216     Py_RETURN_NONE;
217 }
218
219 static PyObject *adm_entries_read(PyObject *self, PyObject *args)
220 {
221     apr_hash_t *entries;
222     AdmObject *admobj = (AdmObject *)self;
223     apr_pool_t *temp_pool;
224     bool show_hidden=false;
225     apr_hash_index_t *idx;
226     const char *key;
227     apr_ssize_t klen;
228     svn_wc_entry_t *entry;
229     PyObject *py_entries, *obj;
230
231     if (!PyArg_ParseTuple(args, "|b", &show_hidden))
232         return NULL;
233
234     ADM_CHECK_CLOSED(admobj);
235
236     temp_pool = Pool(NULL);
237     if (temp_pool == NULL)
238         return NULL;
239     RUN_SVN_WITH_POOL(temp_pool, svn_wc_entries_read(&entries, admobj->adm,
240                                                      show_hidden, temp_pool));
241     py_entries = PyDict_New();
242     if (py_entries == NULL) {
243         apr_pool_destroy(temp_pool);
244         return NULL;
245     }
246     idx = apr_hash_first(temp_pool, entries);
247     while (idx != NULL) {
248         apr_hash_this(idx, (const void **)&key, &klen, (void **)&entry);
249         if (entry == NULL) {
250             obj = Py_None;
251             Py_INCREF(obj);
252         } else {
253             obj = py_entry(entry);
254         }
255         PyDict_SetItemString(py_entries, key, obj);
256         Py_DECREF(obj);
257         idx = apr_hash_next(idx);
258     }
259     apr_pool_destroy(temp_pool);
260     return py_entries;
261 }
262
263 static PyObject *adm_walk_entries(PyObject *self, PyObject *args)
264 {
265     const char *path;
266     PyObject *callbacks;
267     bool show_hidden=false;
268     apr_pool_t *temp_pool;
269     AdmObject *admobj = (AdmObject *)self;
270     svn_depth_t depth = svn_depth_infinity;
271     PyObject *py_path;
272
273     if (!PyArg_ParseTuple(args, "OO|bi", &py_path, &callbacks, &show_hidden, &depth))
274         return NULL;
275
276     ADM_CHECK_CLOSED(admobj);
277
278     temp_pool = Pool(NULL);
279     if (temp_pool == NULL) {
280         return NULL;
281     }
282
283     path = py_object_to_svn_abspath(py_path, temp_pool);
284     if (path == NULL) {
285         apr_pool_destroy(temp_pool);
286         return NULL;
287     }
288
289 #if ONLY_SINCE_SVN(1, 5)
290     RUN_SVN_WITH_POOL(temp_pool, svn_wc_walk_entries3(
291                                                       path, admobj->adm,
292                                                       &py_wc_entry_callbacks2, (void *)callbacks,
293                                                       depth, show_hidden, py_cancel_check, NULL,
294                                                       temp_pool));
295 #else
296     if (depth != svn_depth_infinity) {
297         PyErr_SetString(PyExc_NotImplementedError,
298                         "depth != infinity not supported for svn < 1.5");
299         apr_pool_destroy(temp_pool);
300         return NULL;
301     }
302     RUN_SVN_WITH_POOL(temp_pool, svn_wc_walk_entries2(
303                                                       path, admobj->adm,
304                                                       &py_wc_entry_callbacks, (void *)callbacks,
305                                                       show_hidden, py_cancel_check, NULL,
306                                                       temp_pool));
307 #endif
308     apr_pool_destroy(temp_pool);
309
310     Py_RETURN_NONE;
311 }
312
313 static PyObject *adm_entry(PyObject *self, PyObject *args)
314 {
315     const char *path;
316     PyObject *py_path;
317     bool show_hidden=false;
318     apr_pool_t *temp_pool;
319     AdmObject *admobj = (AdmObject *)self;
320     const svn_wc_entry_t *entry;
321     PyObject *ret;
322
323     if (!PyArg_ParseTuple(args, "O|b", &py_path, &show_hidden))
324         return NULL;
325
326     ADM_CHECK_CLOSED(admobj);
327
328     temp_pool = Pool(NULL);
329     if (temp_pool == NULL) {
330         return NULL;
331     }
332
333     path = py_object_to_svn_abspath(py_path, temp_pool);
334     if (path == NULL) {
335         apr_pool_destroy(temp_pool);
336         return NULL;
337     }
338
339     RUN_SVN_WITH_POOL(temp_pool, svn_wc_entry(&entry, path, admobj->adm, show_hidden, temp_pool));
340
341     if (entry == NULL) {
342         PyErr_Format(PyExc_KeyError, "No such entry '%s'", path);
343         ret = NULL;
344     } else  {
345         ret = py_entry(entry);
346     }
347
348     apr_pool_destroy(temp_pool);
349     return ret;
350 }
351
352 static PyObject *adm_get_prop_diffs(PyObject *self, PyObject *args)
353 {
354     const char *path;
355     apr_pool_t *temp_pool;
356     apr_array_header_t *propchanges;
357     apr_hash_t *original_props;
358     PyObject *py_path;
359     AdmObject *admobj = (AdmObject *)self;
360     PyObject *py_propchanges, *py_orig_props;
361
362     if (!PyArg_ParseTuple(args, "O", &py_path))
363         return NULL;
364
365     ADM_CHECK_CLOSED(admobj);
366
367     temp_pool = Pool(NULL);
368     if (temp_pool == NULL) {
369         return NULL;
370     }
371
372     path = py_object_to_svn_abspath(py_path, temp_pool);
373     if (path == NULL) {
374         apr_pool_destroy(temp_pool);
375         return NULL;
376     }
377     RUN_SVN_WITH_POOL(temp_pool, svn_wc_get_prop_diffs(&propchanges, &original_props,
378                                                        path, admobj->adm, temp_pool));
379     py_propchanges = propchanges_to_list(propchanges);
380     if (py_propchanges == NULL) {
381         apr_pool_destroy(temp_pool);
382         return NULL;
383     }
384     py_orig_props = prop_hash_to_dict(original_props);
385     apr_pool_destroy(temp_pool);
386     if (py_orig_props == NULL) {
387         Py_DECREF(py_propchanges);
388         return NULL;
389     }
390     return Py_BuildValue("(NN)", py_propchanges, py_orig_props);
391 }
392
393 static PyObject *adm_add(PyObject *self, PyObject *args, PyObject *kwargs)
394 {
395     const char *path;
396     const char *copyfrom_url = NULL;
397     svn_revnum_t copyfrom_rev=-1;
398     char *kwnames[] = { "path", "copyfrom_url", "copyfrom_rev", "notify_func", "depth", NULL };
399     PyObject *notify_func=Py_None, *py_path;
400     AdmObject *admobj = (AdmObject *)self;
401     apr_pool_t *temp_pool;
402     svn_depth_t depth = svn_depth_infinity;
403     PyObject *py_copyfrom_url = Py_None;
404
405     if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|OlOi", kwnames, &py_path,
406                                      &py_copyfrom_url, &copyfrom_rev, &notify_func, &depth))
407         return NULL;
408
409     ADM_CHECK_CLOSED(admobj);
410
411     temp_pool = Pool(NULL);
412     if (temp_pool == NULL) {
413         return NULL;
414     }
415
416     path = py_object_to_svn_abspath(py_path, temp_pool);
417     if (path == NULL) {
418         apr_pool_destroy(temp_pool);
419         return NULL;
420     }
421
422     if (py_copyfrom_url != Py_None) {
423         copyfrom_url = py_object_to_svn_uri(py_copyfrom_url, temp_pool);
424         if (copyfrom_url == NULL) {
425             apr_pool_destroy(temp_pool);
426             return NULL;
427         }
428     } else {
429         copyfrom_url = NULL;
430     }
431
432 #if ONLY_SINCE_SVN(1, 6)
433     RUN_SVN_WITH_POOL(temp_pool, svn_wc_add3(
434                                              path, admobj->adm,
435                                              depth, copyfrom_url,
436                                              copyfrom_rev, py_cancel_check, NULL,
437                                              py_wc_notify_func,
438                                              (void *)notify_func,
439                                              temp_pool));
440 #else
441     if (depth != svn_depth_infinity) {
442         PyErr_SetString(PyExc_NotImplementedError, "depth != infinity not supported on svn < 1.6");
443         apr_pool_destroy(temp_pool);
444         return NULL;
445     }
446     RUN_SVN_WITH_POOL(temp_pool, svn_wc_add2(
447                                              path, admobj->adm, copyfrom_url,
448                                              copyfrom_rev, py_cancel_check,
449                                              NULL,
450                                              py_wc_notify_func,
451                                              (void *)notify_func,
452                                              temp_pool));
453 #endif
454     apr_pool_destroy(temp_pool);
455
456     Py_RETURN_NONE;
457 }
458
459 static PyObject *adm_copy(PyObject *self, PyObject *args)
460 {
461     AdmObject *admobj = (AdmObject *)self;
462     char *src, *dst;
463     PyObject *notify_func=Py_None;
464     apr_pool_t *temp_pool;
465
466     if (!PyArg_ParseTuple(args, "ss|O", &src, &dst, &notify_func))
467         return NULL;
468
469     ADM_CHECK_CLOSED(admobj);
470
471     temp_pool = Pool(NULL);
472     if (temp_pool == NULL)
473         return NULL;
474     RUN_SVN_WITH_POOL(temp_pool, svn_wc_copy2(src, admobj->adm, dst,
475                                               py_cancel_check, NULL,
476                                               py_wc_notify_func, (void *)notify_func,
477                                               temp_pool));
478     apr_pool_destroy(temp_pool);
479
480     Py_RETURN_NONE;
481 }
482
483 static PyObject *adm_delete(PyObject *self, PyObject *args, PyObject *kwargs)
484 {
485     AdmObject *admobj = (AdmObject *)self;
486     apr_pool_t *temp_pool;
487     char *kwnames[] = { "path", "notify_func", "keep_local",
488         NULL };
489     const char *path;
490     PyObject *py_path;
491     PyObject *notify_func=Py_None;
492     bool keep_local = false;
493
494     if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|Ob:delete", kwnames,
495                                      &py_path, &notify_func, &keep_local))
496         return NULL;
497
498     ADM_CHECK_CLOSED(admobj);
499
500     temp_pool = Pool(NULL);
501     if (temp_pool == NULL) {
502         return NULL;
503     }
504
505     path = py_object_to_svn_abspath(py_path, temp_pool);
506     if (path == NULL) {
507         apr_pool_destroy(temp_pool);
508         return NULL;
509     }
510
511 #if ONLY_SINCE_SVN(1, 5)
512     RUN_SVN_WITH_POOL(temp_pool, svn_wc_delete3(path, admobj->adm,
513                                                 py_cancel_check, NULL,
514                                                 py_wc_notify_func, (void *)notify_func,
515                                                 keep_local,
516                                                 temp_pool));
517 #else
518     if (keep_local) {
519         PyErr_SetString(PyExc_NotImplementedError,
520                         "keep_local not supported on Subversion < 1.5");
521         return NULL;
522     }
523
524     RUN_SVN_WITH_POOL(temp_pool, svn_wc_delete2(path, admobj->adm,
525                                                 py_cancel_check, NULL,
526                                                 py_wc_notify_func, (void *)notify_func,
527                                                 temp_pool));
528 #endif
529     apr_pool_destroy(temp_pool);
530
531     Py_RETURN_NONE;
532 }
533
534 static PyObject *adm_crawl_revisions(PyObject *self, PyObject *args, PyObject *kwargs)
535 {
536     const char *path;
537     PyObject *reporter;
538     bool restore_files=true, recurse=true, use_commit_times=true;
539     PyObject *notify_func=Py_None;
540     apr_pool_t *temp_pool;
541     AdmObject *admobj = (AdmObject *)self;
542     svn_wc_traversal_info_t *traversal_info;
543     bool depth_compatibility_trick = false;
544     bool honor_depth_exclude = false;
545     char *kwnames[] = { "path", "reporter", "restore_files", "recurse",
546         "use_commit_times", "notify_func", "depth_compatibility_trick",
547         "honor_depth_exclude,", NULL };
548     PyObject *py_path;
549
550     if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OO|bbbObb", kwnames, &py_path,
551                                      &reporter, &restore_files, &recurse, &use_commit_times,
552                                      &notify_func, &depth_compatibility_trick, &honor_depth_exclude))
553         return NULL;
554
555     ADM_CHECK_CLOSED(admobj);
556
557     temp_pool = Pool(NULL);
558     if (temp_pool == NULL) {
559         return NULL;
560     }
561
562     path = py_object_to_svn_abspath(py_path, temp_pool);
563     if (path == NULL) {
564         apr_pool_destroy(temp_pool);
565         return NULL;
566     }
567     traversal_info = svn_wc_init_traversal_info(temp_pool);
568 #if ONLY_SINCE_SVN(1, 6)
569     RUN_SVN_WITH_POOL(temp_pool, svn_wc_crawl_revisions4(path, admobj->adm,
570                                                          &py_ra_reporter3, (void *)reporter,
571                                                          restore_files, recurse?svn_depth_infinity:svn_depth_files,
572                                                          honor_depth_exclude?TRUE:FALSE,
573                                                          depth_compatibility_trick?TRUE:FALSE, use_commit_times,
574                                                          py_wc_notify_func, (void *)notify_func,
575                                                          traversal_info, temp_pool));
576 #elif ONLY_SINCE_SVN(1, 5)
577     RUN_SVN_WITH_POOL(temp_pool, svn_wc_crawl_revisions3(path, admobj->adm,
578                                                          &py_ra_reporter3, (void *)reporter,
579                                                          restore_files, recurse?svn_depth_infinity:svn_depth_files,
580                                                          depth_compatibility_trick, use_commit_times,
581                                                          py_wc_notify_func, (void *)notify_func,
582                                                          traversal_info, temp_pool));
583 #else
584     RUN_SVN_WITH_POOL(temp_pool, svn_wc_crawl_revisions2(path, admobj->adm,
585                                                          &py_ra_reporter2, (void *)reporter,
586                                                          restore_files, recurse, use_commit_times,
587                                                          py_wc_notify_func, (void *)notify_func,
588                                                          traversal_info, temp_pool));
589 #endif
590     apr_pool_destroy(temp_pool);
591
592     Py_RETURN_NONE;
593 }
594
595 static void wc_done_handler(void *self)
596 {
597     AdmObject *admobj = (AdmObject *)self;
598
599     Py_DECREF(admobj);
600 }
601
602 static PyObject *adm_get_switch_editor(PyObject *self, PyObject *args, PyObject *kwargs)
603 {
604     char *target;
605     bool use_commit_times=true;
606     PyObject * notify_func=Py_None;
607     char *diff3_cmd=NULL;
608     const svn_delta_editor_t *editor;
609     AdmObject *admobj = (AdmObject *)self;
610     void *edit_baton;
611     apr_pool_t *pool;
612     svn_revnum_t *latest_revnum;
613     svn_error_t *err;
614     bool allow_unver_obstructions = false;
615     bool depth_is_sticky = false;
616     int depth = svn_depth_infinity;
617     const char *switch_url;
618     PyObject *py_target, *py_switch_url;
619     char *kwnames[] = {
620         "target", "switch_url", "use_commit_times", "depth", "notify_func",
621         "diff3_cmd", "depth_is_sticky", "allow_unver_obstructions", NULL };
622
623     if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OO|biOzbb", kwnames,
624                                      &py_target, &py_switch_url, &use_commit_times,
625                                      &depth, &notify_func, &diff3_cmd,
626                                      &depth_is_sticky,
627                                      &allow_unver_obstructions))
628         return NULL;
629
630     ADM_CHECK_CLOSED(admobj);
631
632     pool = Pool(NULL);
633     if (pool == NULL)
634         return NULL;
635
636     target = py_object_to_svn_string(py_target, pool);
637     if (target == NULL) {
638         apr_pool_destroy(pool);
639         return NULL;
640     }
641
642     switch_url = py_object_to_svn_uri(py_switch_url, pool);
643     if (switch_url == NULL) {
644         apr_pool_destroy(pool);
645         return NULL;
646     }
647
648     latest_revnum = (svn_revnum_t *)apr_palloc(pool, sizeof(svn_revnum_t));
649     Py_BEGIN_ALLOW_THREADS
650 #if ONLY_SINCE_SVN(1, 5)
651         /* FIXME: Support fetch_func */
652         /* FIXME: Support conflict func */
653         err = svn_wc_get_switch_editor3(latest_revnum, admobj->adm, target, switch_url,
654                                         use_commit_times, depth,
655                                         depth_is_sticky?TRUE:FALSE, allow_unver_obstructions?TRUE:FALSE,
656                                         py_wc_notify_func, (void *)notify_func,
657                                         py_cancel_check, NULL,
658                                         NULL, NULL, diff3_cmd, NULL, &editor,
659                                         &edit_baton, NULL, pool);
660 #else
661     if (allow_unver_obstructions) {
662         PyErr_SetString(PyExc_NotImplementedError,
663                         "allow_unver_obstructions is not supported in svn < 1.5");
664         apr_pool_destroy(pool);
665         PyEval_RestoreThread(_save);
666         return NULL;
667     }
668     if (depth_is_sticky) {
669         PyErr_SetString(PyExc_NotImplementedError,
670                         "depth_is_sticky is not supported in svn < 1.5");
671         apr_pool_destroy(pool);
672         PyEval_RestoreThread(_save);
673         return NULL;
674     }
675     err = svn_wc_get_switch_editor2(latest_revnum, admobj->adm, target, switch_url,
676                                     use_commit_times, recurse, py_wc_notify_func, (void *)notify_func,
677                                     py_cancel_check, NULL, diff3_cmd, &editor, &edit_baton,
678                                     NULL, pool);
679 #endif
680     Py_END_ALLOW_THREADS
681         if (err != NULL) {
682             handle_svn_error(err);
683             svn_error_clear(err);
684             apr_pool_destroy(pool);
685             return NULL;
686         }
687     Py_INCREF(admobj);
688     return new_editor_object(NULL, editor, edit_baton, pool, &Editor_Type,
689                              wc_done_handler, admobj, NULL);
690 }
691
692 static PyObject *adm_get_update_editor(PyObject *self, PyObject *args, PyObject *kwargs)
693 {
694     char *target;
695     bool use_commit_times=true;
696     PyObject * notify_func=Py_None;
697     char *diff3_cmd=NULL;
698     const svn_delta_editor_t *editor;
699     AdmObject *admobj = (AdmObject *)self;
700     void *edit_baton;
701     apr_pool_t *pool;
702     svn_revnum_t *latest_revnum;
703     svn_error_t *err;
704     bool allow_unver_obstructions = false;
705     bool depth_is_sticky = false;
706     int depth = svn_depth_infinity;
707     PyObject *py_target;
708     char *kwnames[] = {
709         "target", "use_commit_times", "depth", "notify_func",
710         "diff3_cmd", "depth_is_sticky", "allow_unver_obstructions", NULL };
711
712     if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|biOzbb", kwnames,
713                                      &py_target, &use_commit_times,
714                                      &depth, &notify_func, &diff3_cmd,
715                                      &depth_is_sticky,
716                                      &allow_unver_obstructions))
717         return NULL;
718
719     ADM_CHECK_CLOSED(admobj);
720
721     pool = Pool(NULL);
722     if (pool == NULL)
723         return NULL;
724
725     target = py_object_to_svn_string(py_target, pool);
726     if (target == NULL) {
727         apr_pool_destroy(pool);
728         return NULL;
729     }
730     latest_revnum = (svn_revnum_t *)apr_palloc(pool, sizeof(svn_revnum_t));
731     Py_BEGIN_ALLOW_THREADS
732 #if ONLY_SINCE_SVN(1, 5)
733         /* FIXME: Support fetch_func */
734         /* FIXME: Support conflict func */
735         err = svn_wc_get_update_editor3(latest_revnum, admobj->adm, target,
736                                         use_commit_times, depth,
737                                         depth_is_sticky?TRUE:FALSE, allow_unver_obstructions?TRUE:FALSE,
738                                         py_wc_notify_func, (void *)notify_func,
739                                         py_cancel_check, NULL,
740                                         NULL, NULL, NULL, NULL,
741                                         diff3_cmd, NULL, &editor, &edit_baton,
742                                         NULL, pool);
743 #else
744     if (allow_unver_obstructions) {
745         PyErr_SetString(PyExc_NotImplementedError,
746                         "allow_unver_obstructions is not supported in svn < 1.5");
747         apr_pool_destroy(pool);
748         PyEval_RestoreThread(_save);
749         return NULL;
750     }
751     if (depth_is_sticky) {
752         PyErr_SetString(PyExc_NotImplementedError,
753                         "depth_is_sticky is not supported in svn < 1.5");
754         apr_pool_destroy(pool);
755         PyEval_RestoreThread(_save);
756         return NULL;
757     }
758     err = svn_wc_get_update_editor2(latest_revnum, admobj->adm, target,
759                                     use_commit_times, recurse, py_wc_notify_func, (void *)notify_func,
760                                     py_cancel_check, NULL, diff3_cmd, &editor, &edit_baton,
761                                     NULL, pool);
762 #endif
763     Py_END_ALLOW_THREADS
764         if (err != NULL) {
765             handle_svn_error(err);
766             svn_error_clear(err);
767             apr_pool_destroy(pool);
768             return NULL;
769         }
770     Py_INCREF(admobj);
771     return new_editor_object(NULL, editor, edit_baton, pool, &Editor_Type,
772                              wc_done_handler, admobj, NULL);
773 }
774
775 static PyObject *adm_has_binary_prop(PyObject *self, PyObject *args)
776 {
777     const char *path;
778     svn_boolean_t binary;
779     AdmObject *admobj = (AdmObject *)self;
780     apr_pool_t *temp_pool;
781     PyObject *py_path;
782
783     if (!PyArg_ParseTuple(args, "O", &py_path))
784         return NULL;
785
786     ADM_CHECK_CLOSED(admobj);
787
788     temp_pool = Pool(NULL);
789     if (temp_pool == NULL) {
790         return NULL;
791     }
792
793     path = py_object_to_svn_abspath(py_path, temp_pool);
794     if (path == NULL) {
795         apr_pool_destroy(temp_pool);
796         return NULL;
797     }
798
799     RUN_SVN_WITH_POOL(temp_pool, svn_wc_has_binary_prop(&binary, path, admobj->adm, temp_pool));
800
801     apr_pool_destroy(temp_pool);
802
803     return PyBool_FromLong(binary);
804 }
805
806 static PyObject *adm_process_committed(PyObject *self, PyObject *args, PyObject *kwargs)
807 {
808     const char *path;
809     char *rev_date = NULL, *rev_author = NULL;
810     bool recurse, remove_lock = false;
811     unsigned char *digest = NULL;
812     svn_revnum_t new_revnum;
813     PyObject *py_wcprop_changes = Py_None, *py_path;
814     apr_array_header_t *wcprop_changes = NULL;
815     AdmObject *admobj = (AdmObject *)self;
816     apr_pool_t *temp_pool;
817     int digest_len;
818     bool remove_changelist = false;
819     char *kwnames[] = { "path", "recurse", "new_revnum", "rev_date", "rev_author",
820         "wcprop_changes", "remove_lock", "digest", "remove_changelist", NULL };
821
822     if (!PyArg_ParseTupleAndKeywords(args, kwargs, "Oblzz|Obz#b", kwnames,
823                                      &py_path, &recurse, &new_revnum, &rev_date,
824                                      &rev_author, &py_wcprop_changes,
825                                      &remove_lock, &digest, &digest_len, &remove_changelist))
826         return NULL;
827
828     PyErr_WarnEx(PyExc_DeprecationWarning, "process_committed is deprecated. Use process_committed_queue instead.", 2);
829
830     ADM_CHECK_CLOSED(admobj);
831
832     temp_pool = Pool(NULL);
833     if (temp_pool == NULL) {
834         return NULL;
835     }
836
837     path = py_object_to_svn_abspath(py_path, temp_pool);
838     if (path == NULL) {
839         apr_pool_destroy(temp_pool);
840         return NULL;
841     }
842
843     if (!py_dict_to_wcprop_changes(py_wcprop_changes, temp_pool, &wcprop_changes)) {
844         apr_pool_destroy(temp_pool);
845         return NULL;
846     }
847
848 #if ONLY_SINCE_SVN(1, 6)
849     RUN_SVN_WITH_POOL(temp_pool, svn_wc_process_committed4(
850                                                            path, admobj->adm, recurse, new_revnum,
851                                                            rev_date, rev_author, wcprop_changes,
852                                                            remove_lock, remove_changelist?TRUE:FALSE, digest, temp_pool));
853 #else
854     if (remove_changelist) {
855         PyErr_SetString(PyExc_NotImplementedError, "remove_changelist only supported in svn < 1.6");
856         apr_pool_destroy(temp_pool);
857         return NULL;
858     }
859     RUN_SVN_WITH_POOL(temp_pool, svn_wc_process_committed3(path, admobj->adm, recurse, new_revnum,
860                                                            rev_date, rev_author, wcprop_changes,
861                                                            remove_lock, digest, temp_pool));
862 #endif
863
864     apr_pool_destroy(temp_pool);
865
866     Py_RETURN_NONE;
867 }
868
869 static PyObject *adm_close(PyObject *self)
870 {
871     AdmObject *admobj = (AdmObject *)self;
872     if (admobj->adm != NULL) {
873 #if ONLY_SINCE_SVN(1, 6)
874         apr_pool_t *temp_pool = Pool(NULL);
875         Py_BEGIN_ALLOW_THREADS
876             svn_wc_adm_close2(admobj->adm, temp_pool);
877         apr_pool_destroy(temp_pool);
878 #else
879         Py_BEGIN_ALLOW_THREADS
880             svn_wc_adm_close(admobj->adm);
881 #endif
882         Py_END_ALLOW_THREADS
883             admobj->adm = NULL;
884     }
885
886     Py_RETURN_NONE;
887 }
888
889 static void adm_dealloc(PyObject *self)
890 {
891     apr_pool_destroy(((AdmObject *)self)->pool);
892     PyObject_Del(self);
893 }
894
895 static PyObject *adm_repr(PyObject *self)
896 {
897     AdmObject *admobj = (AdmObject *)self;
898
899     if (admobj->adm == NULL) {
900         return PyRepr_FromFormat("<wc.WorkingCopy (closed) at 0x%p>", admobj);
901     } else {
902         return PyRepr_FromFormat("<wc.WorkingCopy at '%s'>",
903                                  svn_wc_adm_access_path(admobj->adm));
904     }
905 }
906
907 static PyObject *adm_remove_lock(PyObject *self, PyObject *args)
908 {
909     const char *path;
910     PyObject *py_path;
911     AdmObject *admobj = (AdmObject *)self;
912     apr_pool_t *temp_pool;
913
914     if (!PyArg_ParseTuple(args, "O", &py_path))
915         return NULL;
916
917     ADM_CHECK_CLOSED(admobj);
918
919     temp_pool = Pool(NULL);
920     if (temp_pool == NULL) {
921         return NULL;
922     }
923
924     path = py_object_to_svn_abspath(py_path, temp_pool);
925     if (path == NULL) {
926         apr_pool_destroy(temp_pool);
927         return NULL;
928     }
929
930     RUN_SVN_WITH_POOL(temp_pool, svn_wc_remove_lock(path, admobj->adm, temp_pool))
931
932         apr_pool_destroy(temp_pool);
933
934     Py_RETURN_NONE;
935 }
936
937 static PyObject *get_ancestry(PyObject *self, PyObject *args)
938 {
939     const char *path;
940     char *url;
941     svn_revnum_t rev;
942     apr_pool_t *temp_pool;
943     PyObject *py_path;
944     AdmObject *admobj = (AdmObject *)self;
945
946     if (!PyArg_ParseTuple(args, "O", &py_path))
947         return NULL;
948
949     ADM_CHECK_CLOSED(admobj);
950
951     temp_pool = Pool(NULL);
952     if (temp_pool == NULL) {
953         return NULL;
954     }
955
956     path = py_object_to_svn_abspath(py_path, temp_pool);
957     if (path == NULL) {
958         apr_pool_destroy(temp_pool);
959         return NULL;
960     }
961
962     RUN_SVN_WITH_POOL(temp_pool, svn_wc_get_ancestry(&url, &rev, path, admobj->adm, temp_pool));
963
964     apr_pool_destroy(temp_pool);
965
966     return Py_BuildValue("(si)", url, rev);
967 }
968
969 static PyObject *maybe_set_repos_root(PyObject *self, PyObject *args)
970 {
971     char *path, *repos;
972     apr_pool_t *temp_pool;
973     AdmObject *admobj = (AdmObject *)self;
974
975     if (!PyArg_ParseTuple(args, "ss", &path, &repos))
976         return NULL;
977
978     ADM_CHECK_CLOSED(admobj);
979
980     temp_pool = Pool(NULL);
981     if (temp_pool == NULL)
982         return NULL;
983
984     RUN_SVN_WITH_POOL(temp_pool, svn_wc_maybe_set_repos_root(admobj->adm, path, repos, temp_pool));
985
986     Py_RETURN_NONE;
987 }
988
989 static PyObject *add_repos_file(PyObject *self, PyObject *args, PyObject *kwargs)
990 {
991     char *kwnames[] = { "dst_path", "new_base_contents", "new_contents",
992         "new_base_props", "new_props", "copyfrom_url", "copyfrom_rev",
993         "notify", NULL };
994     AdmObject *admobj = (AdmObject *)self;
995     PyObject *py_dst_path;
996     char *copyfrom_url = NULL;
997     svn_revnum_t copyfrom_rev = -1;
998     PyObject *py_new_base_contents, *py_new_contents, *py_new_base_props,
999              *py_new_props, *notify = Py_None;
1000 #if ONLY_SINCE_SVN(1, 6)
1001     apr_pool_t *temp_pool;
1002     const char *dst_path;
1003     svn_stream_t *new_contents, *new_base_contents;
1004     apr_hash_t *new_props, *new_base_props;
1005 #endif
1006
1007     if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OOOOO|zlO", kwnames,
1008                                      &py_dst_path, &py_new_base_contents, &py_new_contents, &py_new_base_props,
1009                                      &py_new_props, &copyfrom_url, &copyfrom_rev, &notify))
1010         return NULL;
1011
1012     ADM_CHECK_CLOSED(admobj);
1013
1014 #if ONLY_SINCE_SVN(1, 6)
1015     temp_pool = Pool(NULL);
1016     if (temp_pool == NULL)
1017         return NULL;
1018
1019     new_base_props = prop_dict_to_hash(temp_pool, py_new_base_props);
1020
1021     new_props = prop_dict_to_hash(temp_pool, py_new_props);
1022
1023     new_base_contents = new_py_stream(temp_pool, py_new_base_contents);
1024
1025     new_contents = new_py_stream(temp_pool, py_new_contents);
1026
1027     dst_path = py_object_to_svn_abspath(py_dst_path, temp_pool);
1028
1029     RUN_SVN_WITH_POOL(temp_pool, svn_wc_add_repos_file3(dst_path, admobj->adm,
1030                                                         new_base_contents,
1031                                                         new_contents,
1032                                                         new_base_props,
1033                                                         new_props,
1034                                                         copyfrom_url, copyfrom_rev,
1035                                                         py_cancel_check, NULL,
1036                                                         py_wc_notify_func, notify, temp_pool));
1037
1038     apr_pool_destroy(temp_pool);
1039
1040     Py_RETURN_NONE;
1041
1042 #else
1043     PyErr_SetString(PyExc_NotImplementedError,
1044                     "add_repos_file3 not supported on svn < 1.6");
1045     return NULL;
1046 #endif
1047 }
1048
1049 static PyObject *mark_missing_deleted(PyObject *self, PyObject *args)
1050 {
1051     const char *path;
1052     AdmObject *admobj = (AdmObject *)self;
1053     apr_pool_t *temp_pool;
1054     PyObject *py_path;
1055
1056     if (!PyArg_ParseTuple(args, "O", &py_path))
1057         return NULL;
1058
1059     ADM_CHECK_CLOSED(admobj);
1060
1061     temp_pool = Pool(NULL);
1062     if (temp_pool == NULL) {
1063         return NULL;
1064     }
1065
1066     path = py_object_to_svn_abspath(py_path, temp_pool);
1067     if (path == NULL) {
1068         apr_pool_destroy(temp_pool);
1069         return NULL;
1070     }
1071
1072     RUN_SVN_WITH_POOL(temp_pool, svn_wc_mark_missing_deleted(path, admobj->adm, temp_pool));
1073
1074     apr_pool_destroy(temp_pool);
1075
1076     Py_RETURN_NONE;
1077 }
1078
1079 static PyObject *remove_from_revision_control(PyObject *self, PyObject *args)
1080 {
1081     char *name;
1082     bool destroy_wf = false, instant_error = false;
1083     AdmObject *admobj = (AdmObject *)self;
1084     apr_pool_t *temp_pool;
1085
1086     if (!PyArg_ParseTuple(args, "s|bb", &name, &destroy_wf, &instant_error))
1087         return NULL;
1088
1089     ADM_CHECK_CLOSED(admobj);
1090
1091     temp_pool = Pool(NULL);
1092     if (temp_pool == NULL)
1093         return NULL;
1094
1095     RUN_SVN_WITH_POOL(temp_pool,
1096                       svn_wc_remove_from_revision_control(admobj->adm, name,
1097                                                           destroy_wf?TRUE:FALSE, instant_error?TRUE:FALSE, py_cancel_check, NULL, temp_pool));
1098
1099     apr_pool_destroy(temp_pool);
1100
1101     Py_RETURN_NONE;
1102 }
1103
1104 static PyObject *relocate(PyObject *self, PyObject *args)
1105 {
1106     const char *path;
1107     char *from, *to;
1108     AdmObject *admobj = (AdmObject *)self;
1109     apr_pool_t *temp_pool;
1110     bool recurse = true;
1111     PyObject *py_validator = Py_None, *py_path;
1112
1113     if (!PyArg_ParseTuple(args, "Oss|bO:relocate", &py_path, &from, &to, &recurse,
1114                           &py_validator))
1115         return NULL;
1116
1117     ADM_CHECK_CLOSED(admobj);
1118
1119     temp_pool = Pool(NULL);
1120     if (temp_pool == NULL) {
1121         return NULL;
1122     }
1123
1124     path = py_object_to_svn_abspath(py_path, temp_pool);
1125     if (path == NULL) {
1126         apr_pool_destroy(temp_pool);
1127         return NULL;
1128     }
1129
1130 #if ONLY_SINCE_SVN(1, 6)
1131     RUN_SVN_WITH_POOL(temp_pool, svn_wc_relocate3(path, admobj->adm, from, to, recurse?TRUE:FALSE, wc_validator3, py_validator, temp_pool));
1132 #else
1133     RUN_SVN_WITH_POOL(temp_pool, svn_wc_relocate2(path, admobj->adm, from, to, recurse?TRUE:FALSE, wc_validator2, py_validator, temp_pool));
1134 #endif
1135
1136     apr_pool_destroy(temp_pool);
1137
1138     Py_RETURN_NONE;
1139 }
1140
1141 static PyObject *crop_tree(PyObject *self, PyObject *args)
1142 {
1143     char *target;
1144     svn_depth_t depth;
1145     PyObject *notify;
1146 #if ONLY_SINCE_SVN(1, 6)
1147     apr_pool_t *temp_pool;
1148 #endif
1149     AdmObject *admobj = (AdmObject *)self;
1150
1151     if (!PyArg_ParseTuple(args, "si|O", &target, &depth, &notify))
1152         return NULL;
1153
1154     ADM_CHECK_CLOSED(admobj);
1155
1156 #if ONLY_SINCE_SVN(1, 6)
1157     temp_pool = Pool(NULL);
1158     if (temp_pool == NULL)
1159         return NULL;
1160
1161     RUN_SVN_WITH_POOL(temp_pool, svn_wc_crop_tree(admobj->adm,
1162                                                   target, depth, py_wc_notify_func, notify,
1163                                                   py_cancel_check, NULL, temp_pool));
1164
1165     apr_pool_destroy(temp_pool);
1166
1167     Py_RETURN_NONE;
1168 #else
1169     PyErr_SetString(PyExc_NotImplementedError,
1170                     "crop_tree only available on subversion < 1.6");
1171     return NULL;
1172 #endif
1173 }
1174
1175 static PyObject *translated_stream(PyObject *self, PyObject *args)
1176 {
1177     char *path, *versioned_file;
1178     StreamObject *ret;
1179     svn_stream_t *stream;
1180     AdmObject *admobj = (AdmObject *)self;
1181     apr_pool_t *stream_pool;
1182     int flags;
1183
1184     if (!PyArg_ParseTuple(args, "ssi", &path, &versioned_file, &flags))
1185         return NULL;
1186
1187     ADM_CHECK_CLOSED(admobj);
1188
1189 #if ONLY_SINCE_SVN(1, 5)
1190     stream_pool = Pool(NULL);
1191     if (stream_pool == NULL)
1192         return NULL;
1193
1194     RUN_SVN_WITH_POOL(stream_pool,
1195                       svn_wc_translated_stream(&stream, path, versioned_file, admobj->adm,
1196                                                flags, stream_pool));
1197
1198     ret = PyObject_New(StreamObject, &Stream_Type);
1199     if (ret == NULL)
1200         return NULL;
1201
1202     ret->pool = stream_pool;
1203     ret->closed = FALSE;
1204     ret->stream = stream;
1205
1206     return (PyObject *)ret;
1207 #else
1208     PyErr_SetString(PyExc_NotImplementedError,
1209                     "translated_stream() is only available on Subversion >= 1.5");
1210     return NULL;
1211 #endif
1212 }
1213
1214 static PyObject *adm_text_modified(PyObject *self, PyObject *args)
1215 {
1216     const char *path;
1217     bool force_comparison = false;
1218     apr_pool_t *temp_pool;
1219     svn_boolean_t ret;
1220     AdmObject *admobj = (AdmObject *)self;
1221     PyObject *py_path;
1222
1223     if (!PyArg_ParseTuple(args, "O|b", &py_path, &force_comparison))
1224         return NULL;
1225
1226     ADM_CHECK_CLOSED(admobj);
1227
1228     temp_pool = Pool(NULL);
1229     if (temp_pool == NULL) {
1230         return NULL;
1231     }
1232
1233     path = py_object_to_svn_abspath(py_path, temp_pool);
1234     if (path == NULL) {
1235         apr_pool_destroy(temp_pool);
1236         return NULL;
1237     }
1238
1239     RUN_SVN_WITH_POOL(temp_pool,
1240                       svn_wc_text_modified_p(&ret, path, force_comparison?TRUE:FALSE, admobj->adm,
1241                                              temp_pool));
1242
1243     apr_pool_destroy(temp_pool);
1244
1245     return PyBool_FromLong(ret);
1246 }
1247
1248 static PyObject *adm_props_modified(PyObject *self, PyObject *args)
1249 {
1250     const char *path;
1251     apr_pool_t *temp_pool;
1252     svn_boolean_t ret;
1253     AdmObject *admobj = (AdmObject *)self;
1254     PyObject *py_path;
1255
1256     if (!PyArg_ParseTuple(args, "O", &py_path))
1257         return NULL;
1258
1259     ADM_CHECK_CLOSED(admobj);
1260
1261     temp_pool = Pool(NULL);
1262     if (temp_pool == NULL) {
1263         return NULL;
1264     }
1265
1266     path = py_object_to_svn_abspath(py_path, temp_pool);
1267     if (path == NULL) {
1268         apr_pool_destroy(temp_pool);
1269         return NULL;
1270     }
1271
1272     RUN_SVN_WITH_POOL(temp_pool,
1273                       svn_wc_props_modified_p(&ret, path, admobj->adm, temp_pool));
1274
1275     apr_pool_destroy(temp_pool);
1276
1277     return PyBool_FromLong(ret);
1278 }
1279
1280 static PyObject *adm_process_committed_queue(PyObject *self, PyObject *args)
1281 {
1282     apr_pool_t *temp_pool;
1283     AdmObject *admobj = (AdmObject *)self;
1284     svn_revnum_t revnum;
1285     char *date, *author;
1286     PyObject *py_queue;
1287
1288     if (!PyArg_ParseTuple(args, "O!lss", &CommittedQueue_Type, &py_queue,
1289                           &revnum, &date, &author))
1290         return NULL;
1291
1292     ADM_CHECK_CLOSED(admobj);
1293
1294     temp_pool = Pool(NULL);
1295     if (temp_pool == NULL)
1296         return NULL;
1297
1298     svn_wc_committed_queue_t *committed_queue = PyObject_GetCommittedQueue(py_queue);
1299
1300 #if ONLY_SINCE_SVN(1, 5)
1301     RUN_SVN_WITH_POOL(temp_pool, svn_wc_process_committed_queue(committed_queue, admobj->adm, revnum, date, author, temp_pool));
1302 #else
1303     {
1304         int i;
1305         for (i = 0; i < py_queue->queue->queue->nelts; i++) {
1306             committed_queue_item_t *cqi = APR_ARRAY_IDX(committed_queue->queue, i,
1307                                                         committed_queue_item_t *);
1308
1309             RUN_SVN_WITH_POOL(temp_pool, svn_wc_process_committed3(cqi->path, admobj->adm,
1310                                                                    cqi->recurse, revnum, date, author, cqi->wcprop_changes,
1311                                                                    cqi->remove_lock, cqi->digest, temp_pool));
1312         }
1313     }
1314 #endif
1315     apr_pool_destroy(temp_pool);
1316
1317     Py_RETURN_NONE;
1318 }
1319
1320
1321 static PyObject *is_wc_root(PyObject *self, PyObject *args)
1322 {
1323     const char *path;
1324     svn_boolean_t wc_root;
1325     apr_pool_t *temp_pool;
1326     AdmObject *admobj = (AdmObject *)self;
1327     PyObject *py_path;
1328
1329     if (!PyArg_ParseTuple(args, "O", &py_path))
1330         return NULL;
1331
1332     ADM_CHECK_CLOSED(admobj);
1333
1334     temp_pool = Pool(NULL);
1335     if (temp_pool == NULL) {
1336         return NULL;
1337     }
1338
1339     path = py_object_to_svn_abspath(py_path, temp_pool);
1340     if (path == NULL) {
1341         apr_pool_destroy(temp_pool);
1342         return NULL;
1343     }
1344
1345     RUN_SVN_WITH_POOL(temp_pool,
1346                       svn_wc_is_wc_root(&wc_root, path, admobj->adm, temp_pool));
1347
1348     apr_pool_destroy(temp_pool);
1349
1350     return PyBool_FromLong(wc_root);
1351 }
1352
1353 static PyObject *transmit_text_deltas(PyObject *self, PyObject *args)
1354 {
1355     const char *path;
1356     const char *tempfile;
1357     bool fulltext;
1358     PyObject *editor_obj, *py_digest, *py_path;
1359     unsigned char digest[APR_MD5_DIGESTSIZE];
1360     apr_pool_t *temp_pool;
1361     AdmObject *admobj = (AdmObject *)self;
1362     PyObject *ret;
1363
1364     if (!PyArg_ParseTuple(args, "ObO", &py_path, &fulltext, &editor_obj))
1365         return NULL;
1366
1367     ADM_CHECK_CLOSED(admobj);
1368
1369     temp_pool = Pool(NULL);
1370     if (temp_pool == NULL) {
1371         return NULL;
1372     }
1373
1374     path = py_object_to_svn_abspath(py_path, temp_pool);
1375     if (path == NULL) {
1376         apr_pool_destroy(temp_pool);
1377         return NULL;
1378     }
1379
1380     Py_INCREF(editor_obj);
1381
1382     RUN_SVN_WITH_POOL(temp_pool,
1383                       svn_wc_transmit_text_deltas2(&tempfile, digest,
1384                                                    path, admobj->adm, fulltext?TRUE:FALSE,
1385                                                    &py_editor, editor_obj, temp_pool));
1386
1387     py_digest = PyBytes_FromStringAndSize((char *)digest, APR_MD5_DIGESTSIZE);
1388     if (py_digest == NULL) {
1389         apr_pool_destroy(temp_pool);
1390         return NULL;
1391     }
1392
1393     ret = Py_BuildValue("sN", tempfile, py_digest);
1394     if (ret == NULL) {
1395         apr_pool_destroy(temp_pool);
1396         return NULL;
1397     }
1398
1399     apr_pool_destroy(temp_pool);
1400
1401     return ret;
1402 }
1403
1404 static PyObject *transmit_prop_deltas(PyObject *self, PyObject *args)
1405 {
1406     const char *path;
1407     PyObject *editor_obj;
1408     apr_pool_t *temp_pool;
1409     AdmObject *admobj = (AdmObject *)self;
1410     PyObject *py_path;
1411     EntryObject *py_entry;
1412
1413     if (!PyArg_ParseTuple(args, "OO!O", &py_path, &Entry_Type, &py_entry, &editor_obj))
1414         return NULL;
1415
1416     ADM_CHECK_CLOSED(admobj);
1417
1418     temp_pool = Pool(NULL);
1419     if (temp_pool == NULL) {
1420         return NULL;
1421     }
1422
1423     path = py_object_to_svn_abspath(py_path, temp_pool);
1424     if (path == NULL) {
1425         apr_pool_destroy(temp_pool);
1426         return NULL;
1427     }
1428
1429     Py_INCREF(editor_obj);
1430
1431     RUN_SVN_WITH_POOL(temp_pool,
1432                       svn_wc_transmit_prop_deltas(path,
1433                                                   admobj->adm, &(py_entry->entry), &py_editor, editor_obj, NULL, temp_pool));
1434
1435     apr_pool_destroy(temp_pool);
1436
1437     Py_RETURN_NONE;
1438 }
1439
1440 static PyObject *retrieve(PyObject *self, PyObject *args)
1441 {
1442     const char *path;
1443     svn_wc_adm_access_t *result;
1444     PyObject *py_path;
1445     AdmObject *admobj = (AdmObject *)self, *ret;
1446     apr_pool_t *pool;
1447
1448     if (!PyArg_ParseTuple(args, "O", &py_path))
1449         return NULL;
1450
1451     ADM_CHECK_CLOSED(admobj);
1452
1453     pool = Pool(NULL);
1454     if (pool == NULL)
1455         return NULL;
1456
1457     path = py_object_to_svn_abspath(py_path, pool);
1458     if (path == NULL) {
1459         apr_pool_destroy(pool);
1460         return NULL;
1461     }
1462
1463     RUN_SVN_WITH_POOL(pool, svn_wc_adm_retrieve(&result, admobj->adm,
1464                                                 path, pool));
1465
1466     ret = PyObject_New(AdmObject, &Adm_Type);
1467     if (ret == NULL)
1468         return NULL;
1469
1470     ret->pool = pool;
1471     ret->adm = result;
1472
1473     return (PyObject *)ret;
1474 }
1475
1476 static PyObject *probe_retrieve(PyObject *self, PyObject *args)
1477 {
1478     const char *path;
1479     svn_wc_adm_access_t *result;
1480     AdmObject *admobj = (AdmObject *)self, *ret;
1481     apr_pool_t *pool;
1482     PyObject *py_path;
1483
1484     if (!PyArg_ParseTuple(args, "O", &py_path))
1485         return NULL;
1486
1487     ADM_CHECK_CLOSED(admobj);
1488
1489     pool = Pool(NULL);
1490     if (pool == NULL) {
1491         return NULL;
1492     }
1493
1494     path = py_object_to_svn_abspath(py_path, pool);
1495     if (path == NULL) {
1496         apr_pool_destroy(pool);
1497         return NULL;
1498     }
1499
1500     RUN_SVN_WITH_POOL(pool, svn_wc_adm_probe_retrieve(&result, admobj->adm,
1501                                                       path, pool));
1502
1503     ret = PyObject_New(AdmObject, &Adm_Type);
1504     if (ret == NULL)
1505         return NULL;
1506
1507     ret->pool = pool;
1508     ret->adm = result;
1509
1510     return (PyObject *)ret;
1511 }
1512
1513 static PyObject *probe_try(PyObject *self, PyObject *args)
1514 {
1515     const char *path;
1516     svn_wc_adm_access_t *result = NULL;
1517     AdmObject *admobj = (AdmObject *)self, *ret;
1518     apr_pool_t *pool;
1519     int levels_to_lock = -1;
1520     bool writelock = false;
1521     PyObject *py_path;
1522
1523     if (!PyArg_ParseTuple(args, "O|bi", &py_path, &writelock, &levels_to_lock))
1524         return NULL;
1525
1526     ADM_CHECK_CLOSED(admobj);
1527
1528     pool = Pool(NULL);
1529     if (pool == NULL) {
1530         return NULL;
1531     }
1532
1533     path = py_object_to_svn_abspath(py_path, pool);
1534     if (path == NULL) {
1535         apr_pool_destroy(pool);
1536         return NULL;
1537     }
1538
1539     RUN_SVN_WITH_POOL(pool, svn_wc_adm_probe_try3(&result, admobj->adm,
1540                                                   path, writelock, levels_to_lock,
1541                                                   py_cancel_check, NULL, pool));
1542
1543     if (result == NULL) {
1544         apr_pool_destroy(pool);
1545         Py_RETURN_NONE;
1546     }
1547
1548     ret = PyObject_New(AdmObject, &Adm_Type);
1549     if (ret == NULL)
1550         return NULL;
1551
1552     ret->pool = pool;
1553     ret->adm = result;
1554
1555     return (PyObject *)ret;
1556 }
1557
1558 static PyObject *resolved_conflict(PyObject *self, PyObject *args)
1559 {
1560     AdmObject *admobj = (AdmObject *)self;
1561     apr_pool_t *temp_pool;
1562     bool resolve_props, resolve_tree, resolve_text;
1563     int depth;
1564 #if ONLY_SINCE_SVN(1, 5)
1565     svn_wc_conflict_choice_t conflict_choice;
1566 #else
1567     int conflict_choice;
1568 #endif
1569     PyObject *notify_func = Py_None;
1570     const char *path;
1571     PyObject *py_path;
1572
1573     if (!PyArg_ParseTuple(args, "Obbbii|O", &py_path, &resolve_text,
1574                           &resolve_props, &resolve_tree, &depth,
1575                           &conflict_choice, &notify_func))
1576         return NULL;
1577
1578     ADM_CHECK_CLOSED(admobj);
1579
1580     temp_pool = Pool(NULL);
1581     if (temp_pool == NULL) {
1582         return NULL;
1583     }
1584
1585     path = py_object_to_svn_abspath(py_path, temp_pool);
1586     if (path == NULL) {
1587         apr_pool_destroy(temp_pool);
1588         return NULL;
1589     }
1590
1591 #if ONLY_SINCE_SVN(1, 6)
1592     RUN_SVN_WITH_POOL(temp_pool,
1593                       svn_wc_resolved_conflict4(path, admobj->adm, resolve_text?TRUE:FALSE,
1594                                                 resolve_props?TRUE:FALSE, resolve_tree?TRUE:FALSE, depth,
1595                                                 conflict_choice, py_wc_notify_func,
1596                                                 (void *)notify_func, py_cancel_check,
1597                                                 NULL, temp_pool));
1598 #elif ONLY_SINCE_SVN(1, 5)
1599     if (resolve_tree) {
1600         PyErr_SetString(PyExc_NotImplementedError,
1601                         "resolve_tree not supported with svn < 1.6");
1602         apr_pool_destroy(temp_pool);
1603         return NULL;
1604     } else {
1605         RUN_SVN_WITH_POOL(temp_pool,
1606                           svn_wc_resolved_conflict3(path, admobj->adm, resolve_text?TRUE:FALSE,
1607                                                     resolve_props?TRUE:FALSE, depth,
1608                                                     conflict_choice, py_wc_notify_func,
1609                                                     (void *)notify_func, py_cancel_check,
1610                                                     NULL, temp_pool));
1611     }
1612 #else
1613     if (resolve_tree) {
1614         PyErr_SetString(PyExc_NotImplementedError,
1615                         "resolve_tree not supported with svn < 1.6");
1616         apr_pool_destroy(temp_pool);
1617         return NULL;
1618     } else if (depth != svn_depth_infinity && depth != svn_depth_files) {
1619         PyErr_SetString(PyExc_NotImplementedError,
1620                         "only infinity and files values for depth are supported");
1621         apr_pool_destroy(temp_pool);
1622         return NULL;
1623     } else if (conflict_choice != 0) {
1624         PyErr_SetString(PyExc_NotImplementedError,
1625                         "conflict choice not supported with svn < 1.5");
1626         apr_pool_destroy(temp_pool);
1627         return NULL;
1628     } else {
1629         RUN_SVN_WITH_POOL(temp_pool,
1630                           svn_wc_resolved_conflict2(path, admobj->adm, resolve_text?TRUE:FALSE,
1631                                                     resolve_props?TRUE:FALSE,
1632                                                     (depth == svn_depth_infinity),
1633                                                     py_wc_notify_func,
1634                                                     (void *)notify_func, py_cancel_check,
1635                                                     NULL,
1636                                                     temp_pool));
1637     }
1638 #endif
1639
1640     apr_pool_destroy(temp_pool);
1641
1642     Py_RETURN_NONE;
1643 }
1644
1645 static PyObject *conflicted(PyObject *self, PyObject *args)
1646 {
1647     const char *path;
1648     apr_pool_t *temp_pool;
1649     PyObject *ret;
1650     AdmObject *admobj = (AdmObject *)self;
1651     svn_boolean_t text_conflicted, prop_conflicted;
1652     PyObject *py_path;
1653 #if ONLY_BEFORE_SVN(1, 6)
1654     const svn_wc_entry_t *entry;
1655 #else
1656     svn_boolean_t tree_conflicted;
1657 #endif
1658
1659     if (!PyArg_ParseTuple(args, "O", &py_path))
1660         return NULL;
1661
1662     ADM_CHECK_CLOSED(admobj);
1663
1664     temp_pool = Pool(NULL);
1665     if (temp_pool == NULL) {
1666         return NULL;
1667     }
1668
1669     path = py_object_to_svn_abspath(py_path, temp_pool);
1670     if (path == NULL) {
1671         apr_pool_destroy(temp_pool);
1672         return NULL;
1673     }
1674
1675 #if ONLY_SINCE_SVN(1, 6)
1676     RUN_SVN_WITH_POOL(temp_pool, svn_wc_conflicted_p2(&text_conflicted,
1677                                                       &prop_conflicted, &tree_conflicted, path, admobj->adm, temp_pool));
1678
1679     ret = Py_BuildValue("(bbb)", text_conflicted, prop_conflicted, tree_conflicted);
1680 #else
1681     RUN_SVN_WITH_POOL(temp_pool, svn_wc_entry(&entry, path, admobj->adm, TRUE, temp_pool));
1682
1683     RUN_SVN_WITH_POOL(temp_pool, svn_wc_conflicted_p(&text_conflicted,
1684                                                      &prop_conflicted, path, entry, temp_pool));
1685
1686     ret = Py_BuildValue("(bbO)", text_conflicted, prop_conflicted, Py_None);
1687 #endif
1688
1689     apr_pool_destroy(temp_pool);
1690
1691     return ret;
1692 }
1693
1694 /**
1695  * Determine the status of a file in the specified working copy.
1696  *
1697  * :return: A status object.
1698  */
1699 static PyObject *wc_status(PyObject *self, PyObject *args)
1700 {
1701     const char *path;
1702     svn_wc_status2_t *st;
1703     apr_pool_t *temp_pool;
1704     PyObject *ret;
1705     AdmObject *admobj = (AdmObject *)self;
1706     PyObject *py_path;
1707
1708     if (!PyArg_ParseTuple(args, "O", &py_path))
1709         return NULL;
1710
1711     ADM_CHECK_CLOSED(admobj);
1712
1713     temp_pool = Pool(NULL);
1714     if (temp_pool == NULL) {
1715         return NULL;
1716     }
1717
1718     path = py_object_to_svn_abspath(py_path, temp_pool);
1719     if (path == NULL) {
1720         apr_pool_destroy(temp_pool);
1721         return NULL;
1722     }
1723
1724     RUN_SVN_WITH_POOL(temp_pool,
1725                       svn_wc_status2(&st, path, admobj->adm, temp_pool));
1726
1727     ret = py_wc_status2(st);
1728
1729     apr_pool_destroy(temp_pool);
1730
1731     return (PyObject*)ret;
1732 }
1733
1734 static PyMethodDef adm_methods[] = {
1735     { "prop_set", adm_prop_set, METH_VARARGS, "S.prop_set(name, value, path, skip_checks=False)" },
1736     { "access_path", (PyCFunction)adm_access_path, METH_NOARGS,
1737         "S.access_path() -> path\n"
1738             "Returns the base path for this working copy handle." },
1739     { "prop_get", adm_prop_get, METH_VARARGS, "S.prop_get(name, path) -> value" },
1740     { "entries_read", adm_entries_read, METH_VARARGS, "S.entries_read(include_hidden=False) -> dict" },
1741     { "walk_entries", adm_walk_entries, METH_VARARGS,
1742         "S.walk_entries(path, callback, show_hidden=False)\n"
1743             "callback should be a function that takes a path and a wc entry" },
1744     { "locked", (PyCFunction)adm_locked, METH_NOARGS,
1745         "S.locked() -> bool" },
1746     { "get_prop_diffs", adm_get_prop_diffs, METH_VARARGS,
1747         "S.get_prop_diffs(path) -> (propchanges, originalprops)" },
1748     { "add", (PyCFunction)adm_add, METH_VARARGS|METH_KEYWORDS, "S.add(path, copyfrom_url=None, copyfrom_rev=-1, notify_func=None)" },
1749     { "copy", adm_copy, METH_VARARGS, "S.copy(src_path, dest_path, notify_func=None)" },
1750     { "delete", (PyCFunction)adm_delete, METH_VARARGS|METH_KEYWORDS, "S.delete(path, notify_func=None, keep_local=False)" },
1751     { "crawl_revisions", (PyCFunction)adm_crawl_revisions, METH_VARARGS|METH_KEYWORDS,
1752         "S.crawl_revisions(path, reporter, restore_files=True, recurse=True, use_commit_times=True, notify_func=None) -> None" },
1753     { "get_update_editor", (PyCFunction)adm_get_update_editor,
1754         METH_VARARGS|METH_KEYWORDS, NULL },
1755     { "get_switch_editor", (PyCFunction)adm_get_switch_editor,
1756         METH_VARARGS|METH_KEYWORDS, NULL },
1757     { "close", (PyCFunction)adm_close, METH_NOARGS,
1758         "S.close()" },
1759     { "entry", (PyCFunction)adm_entry, METH_VARARGS,
1760         "s.entry(path, show_hidden=False) -> entry" },
1761     { "process_committed", (PyCFunction)adm_process_committed, METH_VARARGS|METH_KEYWORDS, "S.process_committed(path, recurse, new_revnum, rev_date, rev_author, wcprop_changes=None, remove_lock=False, digest=None)" },
1762     { "process_committed_queue", (PyCFunction)adm_process_committed_queue, METH_VARARGS, "S.process_committed_queue(queue, new_revnum, rev_date, rev_author)" },
1763     { "remove_lock", (PyCFunction)adm_remove_lock, METH_VARARGS, "S.remove_lock(path)" },
1764     { "has_binary_prop", (PyCFunction)adm_has_binary_prop, METH_VARARGS, "S.has_binary_prop(path) -> bool" },
1765     { "text_modified", (PyCFunction)adm_text_modified, METH_VARARGS, "S.text_modified(filename, force_comparison=False) -> bool" },
1766     { "props_modified", (PyCFunction)adm_props_modified, METH_VARARGS, "S.props_modified(filename) -> bool" },
1767     { "get_ancestry", (PyCFunction)get_ancestry, METH_VARARGS,
1768         "S.get_ancestry(path) -> (url, rev)" },
1769     { "maybe_set_repos_root", (PyCFunction)maybe_set_repos_root, METH_VARARGS, "S.maybe_set_repos_root(path, repos)" },
1770     { "add_repos_file", (PyCFunction)add_repos_file, METH_KEYWORDS,
1771         "S.add_repos_file(dst_path, new_base_contents, new_contents, new_base_props, new_props, copyfrom_url=None, copyfrom_rev=-1, notify_func=None)" },
1772     { "mark_missing_deleted", (PyCFunction)mark_missing_deleted, METH_VARARGS,
1773         "S.mark_missing_deleted(path)" },
1774     { "remove_from_revision_control", (PyCFunction)remove_from_revision_control, METH_VARARGS,
1775         "S.remove_from_revision_control(name, destroy_wf=False, instant_error=False)" },
1776     { "relocate", (PyCFunction)relocate, METH_VARARGS,
1777         "S.relocate(path, from, to, recurse=TRUE, validator=None)" },
1778     { "crop_tree", (PyCFunction)crop_tree, METH_VARARGS,
1779         "S.crop_tree(target, depth, notify_func=None, cancel=None)" },
1780     { "translated_stream", (PyCFunction)translated_stream, METH_VARARGS,
1781         "S.translated_stream(path, versioned_file, flags) -> stream" },
1782     { "is_wc_root", (PyCFunction)is_wc_root, METH_VARARGS,
1783         "S.is_wc_root(path) -> wc_root" },
1784     { "transmit_text_deltas", (PyCFunction)transmit_text_deltas, METH_VARARGS,
1785         "S.transmit_text_deltas(fulltext, editor) -> (tempfile, digest)" },
1786     { "transmit_prop_deltas", (PyCFunction)transmit_prop_deltas, METH_VARARGS,
1787         "S.transmit_prop_deltas(path, entry, editor)" },
1788     { "probe_retrieve", (PyCFunction)probe_retrieve, METH_VARARGS,
1789         "S.probe_retrieve(path) -> WorkingCopy" },
1790     { "retrieve", (PyCFunction)retrieve, METH_VARARGS,
1791         "S.retrieve(path) -> WorkingCopy" },
1792     { "probe_try", (PyCFunction)probe_try, METH_VARARGS,
1793         "S.probe_try(path, write_lock=False, levels_to_lock=-1)" },
1794     { "conflicted", (PyCFunction)conflicted, METH_VARARGS,
1795         "S.conflicted(path) -> (text_conflicted, prop_conflicted, tree_conflicted)" },
1796     { "resolved_conflict", (PyCFunction)resolved_conflict, METH_VARARGS,
1797         "S.resolved_conflict(path, resolve_text, resolve_props, resolve_tree, depth, conflict_choice, notify_func=None, cancel=None)" },
1798     { "status", (PyCFunction)wc_status, METH_VARARGS, "status(wc_path) -> Status" },
1799     { NULL, }
1800 };
1801
1802 PyTypeObject Adm_Type = {
1803     PyVarObject_HEAD_INIT(NULL, 0)
1804         "wc.WorkingCopy", /*    const char *tp_name;  For printing, in format "<module>.<name>" */
1805     sizeof(AdmObject),
1806     0,/*        Py_ssize_t tp_basicsize, tp_itemsize;  For allocation */
1807
1808     /* Methods to implement standard operations */
1809
1810     adm_dealloc, /*     destructor tp_dealloc;  */
1811     NULL, /*    printfunc tp_print;     */
1812     NULL, /*    getattrfunc tp_getattr; */
1813     NULL, /*    setattrfunc tp_setattr; */
1814     NULL, /*    cmpfunc tp_compare;     */
1815     adm_repr, /*        reprfunc tp_repr;       */
1816
1817     /* Method suites for standard classes */
1818
1819     NULL, /*    PyNumberMethods *tp_as_number;  */
1820     NULL, /*    PySequenceMethods *tp_as_sequence;      */
1821     NULL, /*    PyMappingMethods *tp_as_mapping;        */
1822
1823     /* More standard operations (here for binary compatibility) */
1824
1825     NULL, /*    hashfunc tp_hash;       */
1826     NULL, /*    ternaryfunc tp_call;    */
1827     adm_repr, /*        reprfunc tp_repr;       */
1828     NULL, /*    getattrofunc tp_getattro;       */
1829     NULL, /*    setattrofunc tp_setattro;       */
1830
1831     /* Functions to access object as input/output buffer */
1832     NULL, /*    PyBufferProcs *tp_as_buffer;    */
1833
1834     /* Flags to define presence of optional/expanded features */
1835     0, /*       long tp_flags;  */
1836
1837     "Local working copy", /*    const char *tp_doc;  Documentation string */
1838
1839     /* Assigned meaning in release 2.0 */
1840     /* call function for all accessible objects */
1841     NULL, /*    traverseproc tp_traverse;       */
1842
1843     /* delete references to contained objects */
1844     NULL, /*    inquiry tp_clear;       */
1845
1846     /* Assigned meaning in release 2.1 */
1847     /* rich comparisons */
1848     NULL, /*    richcmpfunc tp_richcompare;     */
1849
1850     /* weak reference enabler */
1851     0, /*       Py_ssize_t tp_weaklistoffset;   */
1852
1853     /* Added in release 2.2 */
1854     /* Iterators */
1855     NULL, /*    getiterfunc tp_iter;    */
1856     NULL, /*    iternextfunc tp_iternext;       */
1857
1858     /* Attribute descriptor and subclassing stuff */
1859     adm_methods, /*     struct PyMethodDef *tp_methods; */
1860     NULL, /*    struct PyMemberDef *tp_members; */
1861     NULL, /*    struct PyGetSetDef *tp_getset;  */
1862     NULL, /*    struct _typeobject *tp_base;    */
1863     NULL, /*    PyObject *tp_dict;      */
1864     NULL, /*    descrgetfunc tp_descr_get;      */
1865     NULL, /*    descrsetfunc tp_descr_set;      */
1866     0, /*       Py_ssize_t tp_dictoffset;       */
1867     NULL, /*    initproc tp_init;       */
1868     NULL, /*    allocfunc tp_alloc;     */
1869     adm_init, /*        newfunc tp_new; */
1870 };
1871
1872 static void entry_dealloc(PyObject *self)
1873 {
1874         apr_pool_destroy(((EntryObject *)self)->pool);
1875         PyObject_Del(self);
1876 }
1877
1878 static PyMemberDef entry_members[] = {
1879         { "name", T_STRING, offsetof(EntryObject, entry.name), READONLY,
1880                 "Name of the file"},
1881         { "copyfrom_url", T_STRING, offsetof(EntryObject, entry.copyfrom_url), READONLY,
1882                 "Copyfrom location" },
1883         { "copyfrom_rev", T_LONG, offsetof(EntryObject, entry.copyfrom_rev), READONLY,
1884                 "Copyfrom revision" },
1885         { "uuid", T_STRING, offsetof(EntryObject, entry.uuid), READONLY,
1886                 "UUID of repository" },
1887         { "url", T_STRING, offsetof(EntryObject, entry.url), READONLY,
1888                 "URL in repository" },
1889         { "repos", T_STRING, offsetof(EntryObject, entry.repos), READONLY,
1890                 "Canonical repository URL" },
1891         { "schedule", T_INT, offsetof(EntryObject, entry.schedule), READONLY,
1892                 "Scheduling (add, replace, delete, etc)" },
1893         { "kind", T_INT, offsetof(EntryObject, entry.kind), READONLY,
1894                 "Kind of file (file, dir, etc)" },
1895         { "revision", T_LONG, offsetof(EntryObject, entry.revision), READONLY,
1896                 "Base revision", },
1897         { "cmt_rev", T_LONG, offsetof(EntryObject, entry.cmt_rev), READONLY,
1898                 "Last revision this was changed" },
1899         { "checksum", T_STRING, offsetof(EntryObject, entry.checksum), READONLY,
1900                 "Hex MD5 checksum for the untranslated text base file" },
1901         { "cmt_date", T_LONG, offsetof(EntryObject, entry.cmt_date), READONLY,
1902                 "Last date this was changed" },
1903         { "cmt_author", T_STRING, offsetof(EntryObject, entry.cmt_author), READONLY,
1904                 "Last commit author of this item" },
1905         { NULL, }
1906 };
1907
1908 PyTypeObject Entry_Type = {
1909         PyVarObject_HEAD_INIT(NULL, 0)
1910         "wc.Entry", /*  const char *tp_name;  For printing, in format "<module>.<name>" */
1911         sizeof(EntryObject),
1912         0,/*    Py_ssize_t tp_basicsize, tp_itemsize;  For allocation */
1913
1914         /* Methods to implement standard operations */
1915
1916         entry_dealloc, /*       destructor tp_dealloc;  */
1917         NULL, /*        printfunc tp_print;     */
1918         NULL, /*        getattrfunc tp_getattr; */
1919         NULL, /*        setattrfunc tp_setattr; */
1920         NULL, /*        cmpfunc tp_compare;     */
1921         NULL, /*        reprfunc tp_repr;       */
1922
1923         /* Method suites for standard classes */
1924
1925         NULL, /*        PyNumberMethods *tp_as_number;  */
1926         NULL, /*        PySequenceMethods *tp_as_sequence;      */
1927         NULL, /*        PyMappingMethods *tp_as_mapping;        */
1928
1929         /* More standard operations (here for binary compatibility) */
1930
1931         NULL, /*        hashfunc tp_hash;       */
1932         NULL, /*        ternaryfunc tp_call;    */
1933         NULL, /*        reprfunc tp_str;        */
1934         NULL, /*        getattrofunc tp_getattro;       */
1935         NULL, /*        setattrofunc tp_setattro;       */
1936
1937         /* Functions to access object as input/output buffer */
1938         NULL, /*        PyBufferProcs *tp_as_buffer;    */
1939
1940         /* Flags to define presence of optional/expanded features */
1941         0, /*   long tp_flags;  */
1942
1943         NULL, /*        const char *tp_doc;  Documentation string */
1944
1945         /* Assigned meaning in release 2.0 */
1946         /* call function for all accessible objects */
1947         NULL, /*        traverseproc tp_traverse;       */
1948
1949         /* delete references to contained objects */
1950         NULL, /*        inquiry tp_clear;       */
1951
1952         /* Assigned meaning in release 2.1 */
1953         /* rich comparisons */
1954         NULL, /*        richcmpfunc tp_richcompare;     */
1955
1956         /* weak reference enabler */
1957         0, /*   Py_ssize_t tp_weaklistoffset;   */
1958
1959         /* Added in release 2.2 */
1960         /* Iterators */
1961         NULL, /*        getiterfunc tp_iter;    */
1962         NULL, /*        iternextfunc tp_iternext;       */
1963
1964         /* Attribute descriptor and subclassing stuff */
1965         NULL, /*        struct PyMethodDef *tp_methods; */
1966         entry_members, /*       struct PyMemberDef *tp_members; */
1967
1968 };
1969
1970 static PyObject *py_entry(const svn_wc_entry_t *entry)
1971 {
1972         EntryObject *ret;
1973
1974         if (entry == NULL) {
1975                 Py_RETURN_NONE;
1976         }
1977
1978         ret = PyObject_New(EntryObject, &Entry_Type);
1979         if (ret == NULL)
1980                 return NULL;
1981
1982         ret->pool = Pool(NULL);
1983         if (ret->pool == NULL)
1984                 return NULL;
1985         ret->entry = *svn_wc_entry_dup(entry, ret->pool);
1986         return (PyObject *)ret;
1987 }
1988
1989 typedef struct {
1990         PyObject_VAR_HEAD
1991         apr_pool_t *pool;
1992         svn_wc_status2_t status;
1993         PyObject *entry;
1994 } Status2Object;
1995
1996 static void status_dealloc(PyObject *self)
1997 {
1998         apr_pool_destroy(((Status2Object *)self)->pool);
1999         Py_XDECREF(((Status2Object *)self)->entry);
2000         PyObject_Del(self);
2001 }
2002
2003 static PyMemberDef status_members[] = {
2004         { "entry", T_OBJECT, offsetof(Status2Object, entry), READONLY,
2005                 "Can be NULL if not under version control." },
2006         { "locked", T_BOOL, offsetof(Status2Object, status.locked), READONLY,
2007                 "a directory can be 'locked' if a working copy update was interrupted." },
2008         { "copied", T_BOOL, offsetof(Status2Object, status.copied), READONLY,
2009                 "a file or directory can be 'copied' if it's scheduled for addition-with-history (or part of a subtree that is scheduled as such.)." },
2010         { "switched", T_BOOL, offsetof(Status2Object, status.switched), READONLY,
2011                 "a file or directory can be 'switched' if the switch command has been used." },
2012         { "url", T_STRING, offsetof(Status2Object, status.url), READONLY,
2013                 "URL (actual or expected) in repository" },
2014         { "revision", T_LONG, offsetof(Status2Object, status.ood_last_cmt_rev), READONLY,
2015                 "Set to the youngest committed revision, or SVN_INVALID_REVNUM if not out of date.", },
2016         { "kind", T_INT, offsetof(Status2Object, status.ood_kind), READONLY,
2017                 "Set to the node kind of the youngest commit, or svn_node_none if not out of date.", },
2018         { "status", T_INT, offsetof(Status2Object, status.text_status), READONLY,
2019                 "The status of the entry.", },
2020         { NULL, }
2021 };
2022
2023 PyTypeObject Status2_Type = {
2024         PyVarObject_HEAD_INIT(NULL, 0)
2025         "wc.Status", /* const char *tp_name;  For printing, in format "<module>.<name>" */
2026         sizeof(Status2Object),
2027         0,/*    Py_ssize_t tp_basicsize, tp_itemsize;  For allocation */
2028
2029         /* Methods to implement standard operations */
2030
2031         status_dealloc, /*      destructor tp_dealloc;  */
2032         NULL, /*        printfunc tp_print;     */
2033         NULL, /*        getattrfunc tp_getattr; */
2034         NULL, /*        setattrfunc tp_setattr; */
2035         NULL, /*        cmpfunc tp_compare;     */
2036         NULL, /*        reprfunc tp_repr;       */
2037
2038         /* Method suites for standard classes */
2039
2040         NULL, /*        PyNumberMethods *tp_as_number;  */
2041         NULL, /*        PySequenceMethods *tp_as_sequence;      */
2042         NULL, /*        PyMappingMethods *tp_as_mapping;        */
2043
2044         /* More standard operations (here for binary compatibility) */
2045
2046         NULL, /*        hashfunc tp_hash;       */
2047         NULL, /*        ternaryfunc tp_call;    */
2048         NULL, /*        reprfunc tp_str;        */
2049         NULL, /*        getattrofunc tp_getattro;       */
2050         NULL, /*        setattrofunc tp_setattro;       */
2051
2052         /* Functions to access object as input/output buffer */
2053         NULL, /*        PyBufferProcs *tp_as_buffer;    */
2054
2055         /* Flags to define presence of optional/expanded features */
2056         0, /*   long tp_flags;  */
2057
2058         "Working copy status object", /*        const char *tp_doc;  Documentation string */
2059
2060         /* Assigned meaning in release 2.0 */
2061         /* call function for all accessible objects */
2062         NULL, /*        traverseproc tp_traverse;       */
2063
2064         /* delete references to contained objects */
2065         NULL, /*        inquiry tp_clear;       */
2066
2067         /* Assigned meaning in release 2.1 */
2068         /* rich comparisons */
2069         NULL, /*        richcmpfunc tp_richcompare;     */
2070
2071         /* weak reference enabler */
2072         0, /*   Py_ssize_t tp_weaklistoffset;   */
2073
2074         /* Added in release 2.2 */
2075         /* Iterators */
2076         NULL, /*        getiterfunc tp_iter;    */
2077         NULL, /*        iternextfunc tp_iternext;       */
2078
2079         /* Attribute descriptor and subclassing stuff */
2080         NULL, /*        struct PyMethodDef *tp_methods; */
2081         status_members, /*      struct PyMemberDef *tp_members; */
2082
2083 };
2084
2085 PyObject *py_wc_status2(svn_wc_status2_t *status)
2086 {
2087         Status2Object *ret;
2088         svn_wc_status2_t *dup_status;
2089
2090         ret = PyObject_New(Status2Object, &Status2_Type);
2091         if (ret == NULL)
2092                 return NULL;
2093
2094         ret->pool = Pool(NULL);
2095         if (ret->pool == NULL) {
2096                 PyObject_Del(ret);
2097                 return NULL;
2098         }
2099
2100         dup_status = svn_wc_dup_status2(status, ret->pool);
2101         if (dup_status == NULL)
2102         {
2103                 PyErr_NoMemory();
2104                 return NULL;
2105         }
2106         ret->status = *dup_status;
2107
2108         ret->entry = py_entry(ret->status.entry);
2109         return (PyObject *)ret;
2110 }
2111
2112 static svn_error_t *py_wc_found_entry(const char *path, const svn_wc_entry_t *entry, void *walk_baton, apr_pool_t *pool)
2113 {
2114         PyObject *fn, *ret;
2115         PyObject *callbacks = (PyObject *)walk_baton;
2116         PyGILState_STATE state = PyGILState_Ensure();
2117         if (PyTuple_Check(callbacks)) {
2118                 fn = PyTuple_GET_ITEM(callbacks, 0);
2119         } else {
2120                 fn = (PyObject *)walk_baton;
2121         }
2122         ret = PyObject_CallFunction(fn, "sO", path, py_entry(entry));
2123         CB_CHECK_PYRETVAL(ret);
2124         Py_DECREF(ret);
2125         PyGILState_Release(state);
2126         return NULL;
2127 }
2128
2129 #if ONLY_SINCE_SVN(1, 5)
2130
2131 svn_error_t *py_wc_handle_error(const char *path, svn_error_t *err, void *walk_baton, apr_pool_t *pool)
2132 {
2133         PyObject *fn, *ret;
2134         PyObject *py_err;
2135         PyGILState_STATE state;
2136         PyObject *callbacks = (PyObject *)walk_baton;
2137         if (PyTuple_Check(callbacks)) {
2138                 fn = PyTuple_GET_ITEM(callbacks, 1);
2139         } else {
2140                 return err;
2141         }
2142         state = PyGILState_Ensure();
2143         py_err = PyErr_NewSubversionException(err);
2144         ret = PyObject_CallFunction(fn, "sO", path, py_err);
2145         CB_CHECK_PYRETVAL(ret);
2146         Py_DECREF(ret);
2147         PyGILState_Release(state);
2148         Py_DECREF(py_err);
2149         return NULL;
2150 }
2151
2152 static svn_wc_entry_callbacks2_t py_wc_entry_callbacks2 = {
2153         py_wc_found_entry,
2154         py_wc_handle_error,
2155 };
2156 #else
2157 static svn_wc_entry_callbacks_t py_wc_entry_callbacks = {
2158         py_wc_found_entry
2159 };
2160 #endif
2161
2162 #pragma GCC diagnostic pop