source: trunk/lib/SVN.cc @ 815

Last change on this file since 815 was 693, checked in by Jari Häkkinen, 13 years ago

Fixes #339. Change to GPLv3.

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