source: trunk/lib/SVN.cc @ 519

Last change on this file since 519 was 519, checked in by Jari Häkkinen, 14 years ago

trac moved to new location.

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