source: trunk/lib/SVN.cc @ 1218

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

refs #371. Remove variable binary_ in SVNblame, which was not
used. SVNblame will now fail if ued on binary file (but we don't call
blame on binary files). The SVN class now takes care of svn errors and
translate them into exceptions via the cleanup function.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 11.0 KB
Line 
1// $Id: SVN.cc 1218 2010-10-10 02:27:39Z 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));
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  {
177    assert(message.size()); // compatible with r1213
178    cleanup(err,pool_, message, true);
179    assert(false && "cleanup should throw");
180  }
181
182
183  void SVN::client_blame(const std::string& path,
184                         svn_client_blame_receiver_t receiver,
185                         void *baton, svn_revnum_t rev)
186  {
187    svn_opt_revision_t head;
188    head.kind=svn_opt_revision_number;
189    head.value.number=rev;
190    client_blame_call(path, receiver, baton, head);
191  }
192
193  void SVN::client_blame(const std::string& path,
194                         svn_client_blame_receiver_t receiver,
195                         void *baton)
196  {
197    svn_opt_revision_t head;
198    head.kind = ( svn_path_is_url(path.c_str()) ?
199                  svn_opt_revision_head : svn_opt_revision_base );
200    client_blame_call(path, receiver, baton, head);
201  }
202
203
204  void SVN::client_blame_call(const std::string& path,
205                              svn_client_blame_receiver_t receiver,
206                              void *baton, svn_opt_revision_t& head)
207  {
208    // Setup to use all revisions
209    svn_opt_revision_t peg, start;
210    peg.kind=svn_opt_revision_unspecified;
211    start.kind=svn_opt_revision_number;
212    start.value.number=0;
213    apr_pool_t *subpool = svn_pool_create(pool_);
214    svn_error_t* err=svn_client_blame3(path.c_str(), &peg, &start, &head,
215                                       svn_diff_file_options_create(subpool),
216                                       false, receiver, baton, context_,
217                                       subpool);
218    if (err)
219      // cleanup will throw an exception
220      cleanup(err, subpool, "SVN::client_blame: svn_client_blame3 failed");
221    svn_pool_destroy(subpool);
222  }
223
224
225  void SVN::client_info(const std::string& path, svn_info_receiver_t receiver,
226                        void *baton)
227  {
228    apr_pool_t *subpool = svn_pool_create(pool_);
229    if (svn_error_t *err=svn_client_info(path.c_str(), NULL, NULL, receiver,
230                                         baton, false, context_, subpool))
231      // cleanup will throw an exception
232      cleanup(err, subpool, "repository: svn_client_info failed");
233    svn_pool_destroy(subpool);
234  }
235
236
237  void SVN::client_log(const std::string& path,
238                       svn_log_message_receiver_t receiver, void *baton)
239  {
240    // Allocate space in subpool to pool_ for apr_path (here a string).
241    apr_pool_t *subpool = svn_pool_create(pool_);
242    apr_array_header_t* apr_path=apr_array_make(subpool,1,4);
243    // Copy path to apr_path.
244    (*((const char **) apr_array_push(apr_path))) =
245      apr_pstrdup(subpool, svn_path_internal_style(path.c_str(),subpool));
246
247    // Setup to retrieve all commit logs.
248    svn_opt_revision_t peg, start, head;
249    peg.kind=svn_opt_revision_unspecified;
250    start.kind=svn_opt_revision_number;
251    start.value.number=0;
252    head.kind = ( svn_path_is_url(path.c_str()) ?
253                  svn_opt_revision_head : svn_opt_revision_base );
254    svn_error_t* err=NULL;
255    if ((err=svn_client_log3(apr_path, &peg, &start, &head, 0, false, false,
256                             receiver, baton, context_, subpool)))
257      // cleanupp will throw an exception
258      cleanup(err, subpool, "commit_dates: svn_client_log3 failed");
259    svn_pool_destroy(subpool);
260  }
261
262
263  void SVN::client_proplist(const std::string& path,
264                            std::map<std::string, std::string>& property)
265  {
266    svn_opt_revision_t peg, revision;
267    peg.kind=svn_opt_revision_unspecified;
268    revision.kind = ( svn_path_is_url(path.c_str()) ?
269                      svn_opt_revision_head : svn_opt_revision_base );
270    apr_pool_t *subpool = svn_pool_create(pool_);
271    apr_array_header_t * properties;
272    svn_error_t *err=svn_client_proplist2(&properties, path.c_str(), &peg,
273                                          &revision, false, context_, subpool);
274    if (err)
275      cleanup(err, subpool, "repository: svn_client_proplist2 failed");
276
277    for (int j = 0; j < properties->nelts; ++j) {
278      svn_client_proplist_item_t *item = 
279        ((svn_client_proplist_item_t **)properties->elts)[j];
280      for (apr_hash_index_t *hi = apr_hash_first(subpool, item->prop_hash); hi; 
281           hi = apr_hash_next (hi)) {
282        const void *key;
283        void *val;
284        apr_hash_this (hi, &key, NULL, &val);
285        svn_string_t *value;
286        err=svn_subst_detranslate_string(&value,
287                                         static_cast<const svn_string_t*>(val),
288                                         false, subpool);
289        if (err)
290          cleanup(err, subpool,
291                  path +
292                  " property: svn_subst_detranslate_string failed on key " +
293                  static_cast<const char*>(key));
294        property[static_cast<const char*>(key)]=value->data;
295      }
296    }
297    svn_pool_destroy(subpool);
298  }
299
300
301  SVN* SVN::instance(void)
302  {
303    if (!instance_)
304      throw SVNException("SVN::instance(void): SVN singleton not initialized");
305    return instance_;
306  }
307
308
309  SVN* SVN::instance(const std::string& url)
310  {
311    if (!instance_) {
312      instance_=new SVN(url);
313    }
314    return instance_;
315  }
316
317
318  svn_error_t* SVN::root_url_receiver(void *baton, const char *,
319                                      const svn_info_t *info, apr_pool_t*)
320  {
321    if (!info)
322      throw SVNException(std::string("SVN::url_receriver: ") +
323                         "Failed to acquire an svn info object");
324
325    root_url_receiver_baton* rurb=
326      static_cast<struct root_url_receiver_baton*>(baton);
327    if (info->repos_root_URL)
328      rurb->path=info->repos_root_URL;
329    return SVN_NO_ERROR;
330  }
331
332
333  SVN::vc_status SVN::version_controlled(const std::string& path)
334  {
335    svn_wc_status2_t* status=NULL;
336    apr_pool_t *subpool = svn_pool_create(pool_);
337    if (svn_error_t *err=
338        svn_wc_status2(&status,svn_path_internal_style(path.c_str(), subpool),
339                       adm_access_, subpool))
340      // cleanup will throw an exception
341      cleanup(err, subpool,"version_controlled(): svn_config_get_config failed");
342    svn_pool_destroy(subpool);
343
344    if ((status->text_status==svn_wc_status_none) ||
345        (status->text_status==svn_wc_status_unversioned))
346      return unversioned;
347    else if (status->text_status==svn_wc_status_normal)
348      return uptodate;
349
350    return unresolved;
351  }
352
353}} // end of namespace svndigest and namespace theplu
Note: See TracBrowser for help on using the repository browser.