source: branches/0.8-stable/lib/SVN.cc @ 1199

Last change on this file since 1199 was 978, checked in by Peter Johansson, 11 years ago

refs #429. Convert copyright statements to UTF-8

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