source: trunk/lib/SVN.cc @ 1221

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

fix error_test.sh failure on Linux associated with problems in SVNException. Avoid returning const char* to temporary string. Perform arithmetic on int not int*.

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