source: trunk/lib/SVN.cc @ 1216

Last change on this file since 1216 was 1216, checked in by Peter Johansson, 12 years ago

refs #371. avoid using svn_handle_error2 and communicate through exception class instead.

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