source: trunk/lib/SVN.cc @ 1373

Last change on this file since 1373 was 1373, checked in by Peter Johansson, 10 years ago

merging patch release 0.9.2 into trunk

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 11.3 KB
Line 
1// $Id: SVN.cc 1373 2011-06-10 23:31:35Z peter $
2
3/*
4  Copyright (C) 2006 Jari Häkkinen
5  Copyright (C) 2007, 2008 Jari Häkkinen, Peter Johansson
6  Copyright (C) 2010, 2011 Peter Johansson
7
8  This file is part of svndigest, http://dev.thep.lu.se/svndigest
9
10  svndigest is free software; you can redistribute it and/or modify it
11  under the terms of the GNU General Public License as published by
12  the Free Software Foundation; either version 3 of the License, or
13  (at your option) any later version.
14
15  svndigest is distributed in the hope that it will be useful, but
16  WITHOUT ANY WARRANTY; without even the implied warranty of
17  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18  General Public License for more details.
19
20  You should have received a copy of the GNU General Public License
21  along with svndigest. If not, see <http://www.gnu.org/licenses/>.
22*/
23
24#include "SVN.h"
25
26#include <map>
27#include <string>
28#include <vector>
29
30#include <cassert>
31
32#include <apr_allocator.h>
33#include <apr_hash.h>
34#include <subversion-1/svn_client.h>
35#include <subversion-1/svn_cmdline.h>
36#include <subversion-1/svn_path.h>
37#include <subversion-1/svn_pools.h>
38#include <subversion-1/svn_wc.h>
39#include <subversion-1/svn_subst.h>
40
41#include <string>
42
43namespace theplu {
44namespace svndigest {
45
46
47  SVNException::SVNException(const std::string& msg, svn_error_t* error)
48    : std::runtime_error(""), error_(error)
49  {
50    ref_count_ = new int(1);
51    if (error_) {
52      apr_size_t bufsize=255;
53      char buf[255];
54      msg_ = svn_err_best_message(error_, buf, bufsize);
55    }
56    if (msg.size()) {
57      msg_ += msg;
58    }
59  }
60
61  SVNException::SVNException(const SVNException& other)
62    : std::runtime_error(other), error_(other.error_), msg_(other.msg_),
63      ref_count_(other.ref_count_)
64  {
65    ++(*ref_count_);
66  }
67
68
69  SVNException::~SVNException(void) throw() 
70  {
71    --(*ref_count_);
72    if (!(*ref_count_)) {
73      delete ref_count_;
74      ref_count_=NULL;
75      svn_error_clear(error_);
76      error_=SVN_NO_ERROR;
77    }
78  }
79
80
81  const char* SVNException::what(void) const throw()
82  {
83    return msg_.c_str();
84  }
85
86
87  const svn_error_t* SVNException::error(void) const
88  {
89    return error_;
90  }
91
92
93  SVN* SVN::instance_=NULL;
94
95
96  SVN::SVN(const std::string& path)
97    : adm_access_(NULL), allocator_(NULL), context_(NULL), pool_(NULL)
98  {
99    svn_error_t* err=NULL;
100
101    // initialize something (APR subsystem and more). The APR
102    // subsystem is automatically destroyed at program exit. In case
103    // of several calls to svn_cmdline_init (ie. several exceptions
104    // thrown and caught with subsequent reinitializatios is safe
105    // memorywise but what about APR internal counters?)
106    if (svn_cmdline_init("svndigest",stderr) != EXIT_SUCCESS)
107      throw SVNException("SVN: svn_cmdline_init failed");
108
109    /// create top-level pool
110    if (apr_allocator_create(&allocator_))
111      throw SVNException("SVN(_allocator_create failed");
112    apr_allocator_max_free_set(allocator_,SVN_ALLOCATOR_RECOMMENDED_MAX_FREE);
113    pool_ = svn_pool_create_ex(NULL, allocator_);
114    apr_allocator_owner_set(allocator_, pool_);
115
116    // initialize the repository access library
117    if ((err=svn_ra_initialize(pool_)))
118      cleanup_failed_init(err, "SVN: svn_ra_initialize failed");
119
120    // Check that users .subversion exist. Should this really be done?
121    // If this check is to be done, we might be forced to support a
122    // command line option to change the location of the .subversion
123    // stuff (compare with the svn binary).
124    if ((err=svn_config_ensure(NULL, pool_)))
125      cleanup_failed_init(err, "SVN: svn_config_ensure failed");
126
127    // create a client context object
128    if ((err=svn_client_create_context(&context_, pool_)))
129      cleanup_failed_init(err, "SVN: svn_client_create_context failed");
130
131    if ((err=svn_config_get_config(&(context_->config), NULL, pool_)))
132      cleanup_failed_init(err, "SVN: svn_config_get_config failed");
133
134    // set up authentication stuff
135    if ((err=svn_cmdline_setup_auth_baton(&(context_->auth_baton), false, NULL,
136                                          NULL, NULL, false,
137                        static_cast<svn_config_t*>(apr_hash_get(context_->config,
138                                                    SVN_CONFIG_CATEGORY_CONFIG,
139                                                    APR_HASH_KEY_STRING)),
140                                          context_->cancel_func,
141                                          context_->cancel_baton, pool_)))
142      cleanup_failed_init(err, "SVN: svn_cmdline_setup_auth_baton failed");
143
144    // Set up svn administration area access. The whole structure is
145    // setup, maybe this is unnecessary?
146    const char* canonical_path=svn_path_internal_style(path.c_str(), pool_);
147    if ((err=svn_wc_adm_open3(&adm_access_, NULL, canonical_path,
148                              false, -1, context_->cancel_func,
149                              context_->cancel_baton, pool_))){
150      if (err->apr_err == SVN_ERR_WC_NOT_DIRECTORY)
151        cleanup_failed_init(err, std::string(err->message));
152      cleanup_failed_init(err, "SVN: svn_wc_adm_open3 failed");
153    }
154
155    // get a session to the repository
156    struct root_url_receiver_baton rurb;
157    client_info(path, root_url_receiver, static_cast<void*>(&rurb));
158    if ((err=svn_client_open_ra_session(&ra_session_,
159                                        rurb.path.c_str(),
160                                        context_, pool_)))
161      cleanup_failed_init(err, "SVN: svn_client_open_ra_session failed");
162  }
163
164
165  SVN::~SVN(void)
166  {
167    if (adm_access_)
168      svn_error_clear(svn_wc_adm_close(adm_access_));
169    svn_pool_destroy(pool_);
170    pool_=NULL;
171    // other apr resources acquired in svn_cmdline_init are destroyed
172    // at program exit, ok since SVN is a singleton
173  }
174
175
176  void SVN::cleanup(svn_error_t *err,apr_pool_t *pool,
177                    std::string message, bool mute)
178  {
179    svn_error_clear(err);
180    if (pool){
181      svn_pool_destroy(pool);
182      pool=NULL;
183    }
184    assert(message.size()); // compatible with r1213
185    if (!mute)
186      throw SVNException(message, err);
187    // mute implies we don't wanna hear the message from svn_error_t
188    throw SVNException(message);
189  }
190
191
192  void SVN::cleanup_failed_init(svn_error_t *err, const std::string& message)
193  {
194    assert(message.size()); // compatible with r1213
195    cleanup(err,pool_, message, true);
196    assert(false && "cleanup should throw");
197  }
198
199
200  void SVN::client_blame(const std::string& path,
201                         svn_client_blame_receiver_t receiver,
202                         void *baton, svn_revnum_t rev)
203  {
204    svn_opt_revision_t head;
205    head.kind=svn_opt_revision_number;
206    head.value.number=rev;
207    client_blame_call(path, receiver, baton, head);
208  }
209
210  void SVN::client_blame(const std::string& path,
211                         svn_client_blame_receiver_t receiver,
212                         void *baton)
213  {
214    svn_opt_revision_t head;
215    head.kind = ( svn_path_is_url(path.c_str()) ?
216                  svn_opt_revision_head : svn_opt_revision_base );
217    client_blame_call(path, receiver, baton, head);
218  }
219
220
221  void SVN::client_blame_call(const std::string& path,
222                              svn_client_blame_receiver_t receiver,
223                              void *baton, svn_opt_revision_t& head)
224  {
225    // Setup to use all revisions
226    svn_opt_revision_t peg, start;
227    peg.kind=svn_opt_revision_unspecified;
228    start.kind=svn_opt_revision_number;
229    start.value.number=0;
230    apr_pool_t *subpool = svn_pool_create(pool_);
231    svn_error_t* err=svn_client_blame3(path.c_str(), &peg, &start, &head,
232                                       svn_diff_file_options_create(subpool),
233                                       false, receiver, baton, context_,
234                                       subpool);
235    if (err)
236      // cleanup will throw an exception
237      cleanup(err, subpool, "SVN::client_blame: svn_client_blame3 failed");
238    svn_pool_destroy(subpool);
239  }
240
241
242  void SVN::client_info(const std::string& path, svn_info_receiver_t receiver,
243                        void *baton)
244  {
245    apr_pool_t *subpool = svn_pool_create(pool_);
246    if (svn_error_t *err=svn_client_info(path.c_str(), NULL, NULL, receiver,
247                                         baton, false, context_, subpool))
248      // cleanup will throw an exception
249      cleanup(err, subpool, "repository: svn_client_info failed");
250    svn_pool_destroy(subpool);
251  }
252
253
254  void SVN::client_log(const std::string& path,
255                       svn_log_message_receiver_t receiver, void *baton)
256  {
257    // Allocate space in subpool to pool_ for apr_path (here a string).
258    apr_pool_t *subpool = svn_pool_create(pool_);
259    apr_array_header_t* apr_path=apr_array_make(subpool,1,4);
260    // Copy path to apr_path.
261    (*((const char **) apr_array_push(apr_path))) =
262      apr_pstrdup(subpool, svn_path_internal_style(path.c_str(),subpool));
263
264    // Setup to retrieve all commit logs.
265    svn_opt_revision_t peg, start, head;
266    peg.kind=svn_opt_revision_unspecified;
267    start.kind=svn_opt_revision_number;
268    start.value.number=0;
269    head.kind = ( svn_path_is_url(path.c_str()) ?
270                  svn_opt_revision_head : svn_opt_revision_base );
271    svn_error_t* err=NULL;
272    if ((err=svn_client_log3(apr_path, &peg, &start, &head, 0, false, false,
273                             receiver, baton, context_, subpool)))
274      // cleanupp will throw an exception
275      cleanup(err, subpool, "commit_dates: svn_client_log3 failed");
276    svn_pool_destroy(subpool);
277  }
278
279
280  void SVN::client_proplist(const std::string& path,
281                            std::map<std::string, std::string>& property)
282  {
283    svn_opt_revision_t peg, revision;
284    peg.kind=svn_opt_revision_unspecified;
285    revision.kind = ( svn_path_is_url(path.c_str()) ?
286                      svn_opt_revision_head : svn_opt_revision_base );
287    apr_pool_t *subpool = svn_pool_create(pool_);
288    apr_array_header_t * properties;
289    svn_error_t *err=svn_client_proplist2(&properties, path.c_str(), &peg,
290                                          &revision, false, context_, subpool);
291    if (err)
292      cleanup(err, subpool, "repository: svn_client_proplist2 failed");
293
294    for (int j = 0; j < properties->nelts; ++j) {
295      svn_client_proplist_item_t *item = 
296        ((svn_client_proplist_item_t **)properties->elts)[j];
297      for (apr_hash_index_t *hi = apr_hash_first(subpool, item->prop_hash); hi; 
298           hi = apr_hash_next (hi)) {
299        const void *key;
300        void *val;
301        apr_hash_this (hi, &key, NULL, &val);
302        svn_string_t *value;
303        err=svn_subst_detranslate_string(&value,
304                                         static_cast<const svn_string_t*>(val),
305                                         false, subpool);
306        if (err)
307          cleanup(err, subpool,
308                  path +
309                  " property: svn_subst_detranslate_string failed on key " +
310                  static_cast<const char*>(key));
311        property[static_cast<const char*>(key)]=value->data;
312      }
313    }
314    svn_pool_destroy(subpool);
315  }
316
317
318  SVN* SVN::instance(void)
319  {
320    if (!instance_)
321      throw SVNException("SVN::instance(void): SVN singleton not initialized");
322    return instance_;
323  }
324
325
326  SVN* SVN::instance(const std::string& url)
327  {
328    if (!instance_) {
329      instance_=new SVN(url);
330    }
331    return instance_;
332  }
333
334
335  svn_error_t* SVN::root_url_receiver(void *baton, const char *,
336                                      const svn_info_t *info, apr_pool_t*)
337  {
338    if (!info)
339      throw SVNException(std::string("SVN::url_receriver: ") +
340                         "Failed to acquire an svn info object");
341
342    root_url_receiver_baton* rurb=
343      static_cast<struct root_url_receiver_baton*>(baton);
344    if (info->repos_root_URL)
345      rurb->path=info->repos_root_URL;
346    return SVN_NO_ERROR;
347  }
348
349
350  SVN::vc_status SVN::version_controlled(const std::string& path)
351  {
352    svn_wc_status2_t* status=NULL;
353    apr_pool_t *subpool = svn_pool_create(pool_);
354    if (svn_error_t *err=
355        svn_wc_status2(&status,svn_path_internal_style(path.c_str(), subpool),
356                       adm_access_, subpool))
357      // cleanup will throw an exception
358      cleanup(err, subpool,"version_controlled(): svn_config_get_config failed");
359    svn_pool_destroy(subpool);
360
361    if ((status->text_status==svn_wc_status_none) ||
362        (status->text_status==svn_wc_status_unversioned))
363      return unversioned;
364    else if (status->text_status==svn_wc_status_normal)
365      return uptodate;
366
367    return unresolved;
368  }
369
370}} // end of namespace svndigest and namespace theplu
Note: See TracBrowser for help on using the repository browser.