source: trunk/lib/SVN.cc @ 140

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

Addresses #56. Fixed freeing of resources on svn error.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 8.7 KB
Line 
1// $Id: SVN.cc 140 2006-08-04 00:12:11Z jari $
2
3/*
4  Copyright (C) 2006 Jari Häkkinen
5
6  This file is part of svnstat, http://lev.thep.lu.se/trac/svnstat
7
8  svnstat is free software; you can redistribute it and/or modify it
9  under the terms of the GNU General Public License as published by
10  the Free Software Foundation; either version 2 of the License, or
11  (at your option) any later version.
12
13  svnstat is distributed in the hope that it will be useful, but
14  WITHOUT ANY WARRANTY; without even the implied warranty of
15  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  General Public License for more details.
17
18  You should have received a copy of the GNU General Public License
19  along with this program; if not, write to the Free Software
20  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
21  02111-1307, USA.
22*/
23
24#include "SVN.h"
25
26#include <string>
27
28#include <apr_allocator.h>
29#include <apr_hash.h>
30#include <svn_cmdline.h>
31#include <svn_path.h>
32#include <svn_pools.h>
33#include <svn_wc.h>
34
35namespace theplu {
36namespace svnstat {
37
38
39  SVN* SVN::instance_=NULL;
40
41
42  SVN::SVN(void)
43    : adm_access_(NULL), allocator_(NULL), context_(NULL), pool_(NULL)
44  {
45    // To be honest, I (Jari) am not perfectly sure of what I am
46    // doing, most bits here are stolen from subversion source for the
47    // svn command line binary (subverion/svn/main.c)
48
49    svn_error_t* err=NULL;
50
51    // initialize something (APR subsystem and more). The APR
52    // subsystem is automatically destroyed at program exit. In case
53    // of several calls to svn_cmdline_init (ie. several exceptions
54    // thrown and caught with subsequent reinitializatios is safe
55    // memorywise but what about APR internal counters?)
56    if (svn_cmdline_init("svnstat",stderr) != EXIT_SUCCESS)
57      throw SVNException("SVN(void): svn_cmdline_init failed");
58
59    /// create top-level pool
60    if (apr_allocator_create(&allocator_))
61      throw SVNException("SVN(void): apr_allocator_create failed");
62    apr_allocator_max_free_set(allocator_,SVN_ALLOCATOR_RECOMMENDED_MAX_FREE);
63    pool_ = svn_pool_create_ex(NULL, allocator_);
64    apr_allocator_owner_set(allocator_, pool_);
65
66    // initialize the repository access library
67    if ((err=svn_ra_initialize(pool_))) {
68      cleanup_failed_initialization(err);
69      throw SVNException("SVN(void): svn_ra_initialize failed");
70    }
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_initialization(err);
78      throw SVNException("SVN(void): svn_config_ensure failed");
79    }
80
81    // create a client context object
82    if ((err=svn_client_create_context(&context_, pool_))) {
83      cleanup_failed_initialization(err);
84      throw SVNException("SVN(void): svn_client_create_context failed");
85    }
86
87    if ((err=svn_config_get_config(&(context_->config), NULL, pool_))) {
88      cleanup_failed_initialization(err);
89      throw SVNException("SVN(void): svn_config_get_config failed");
90    }
91
92    // set up authentication stuff
93    if ((err=svn_cmdline_setup_auth_baton(&(context_->auth_baton), false, NULL,
94                                          NULL, NULL, false,
95            static_cast<svn_config_t*>(apr_hash_get(context_->config,
96                                                    SVN_CONFIG_CATEGORY_CONFIG,
97                                                    APR_HASH_KEY_STRING)),
98                                          context_->cancel_func,
99                                          context_->cancel_baton, pool_))) {
100      cleanup_failed_initialization(err);
101      throw SVNException("SVN(void): svn_cmdline_setup_auth_baton failed");
102    }
103  }
104
105
106  SVN::~SVN(void)
107  {
108    if (adm_access_)
109      svn_error_clear(svn_wc_adm_close(adm_access_));
110    svn_pool_destroy(pool_);
111    apr_allocator_destroy(allocator_);
112    // other apr resources acquired in svn_cmdline_init are destroyed
113    // at program exit, ok since SVN is a singleton
114    delete instance_;
115  }
116
117
118  svn_error_t* SVN::client_blame(const std::string& path,
119                                 svn_client_blame_receiver_t receiver,
120                                 void *baton)
121  {
122    // Setup to use all revisions
123    svn_opt_revision_t peg, start, head;
124    peg.kind=svn_opt_revision_unspecified;
125    start.kind=svn_opt_revision_number;
126    start.value.number=0;
127    head.kind=svn_opt_revision_head;
128    svn_error_t* err=svn_client_blame3(path.c_str(), &peg, &start, &head,
129                                       // Jari, below line creates memory leaks
130                                       svn_diff_file_options_create(pool_),
131                                       false, receiver, baton, context_, pool_);
132    if (err && err->apr_err!=SVN_ERR_CLIENT_IS_BINARY_FILE) {
133      svn_handle_error2(err, stderr, false, "svnstat: ");
134      svn_error_clear(err);
135      throw SVNException("SVN::client_blame: svn_client_blame3 failed");
136    }
137    return err;
138  }
139
140
141  void SVN::client_info(const std::string& path, svn_info_receiver_t receiver,
142                        void *baton)
143  {
144    if (svn_error_t *err=svn_client_info(path.c_str(), NULL, NULL, receiver,
145                                         baton, false, context_, pool_)) {
146      svn_handle_error2(err, stderr, false, "svnstat: ");
147      svn_error_clear(err);
148      throw SVNException("repository: svn_client_info failed");
149    }
150  }
151
152
153  std::vector<std::string> SVN::commit_dates(const std::string& path)
154  {
155    // Allocate space in pool_ for apr_path (here a string).
156    apr_array_header_t* apr_path=apr_array_make(pool_,1,4);
157    // Copy path to apr_path.
158    (*((const char **) apr_array_push(apr_path))) =
159      apr_pstrdup(pool_, svn_path_internal_style(path.c_str(),pool_));
160
161    // Setup to retrieve all commit logs.
162    svn_opt_revision_t peg, start, head;
163    peg.kind=svn_opt_revision_unspecified;
164    start.kind=svn_opt_revision_number;
165    start.value.number=0;
166    head.kind=svn_opt_revision_head;
167    svn_error_t* err=NULL;
168    // Retrieving the last revision is only needed for the reserve
169    // call below, not needed for the functionality here.
170    if ((err=svn_ra_get_latest_revnum(ra_session_,&(head.value.number),pool_))) {
171      svn_handle_error2(err, stderr, false, "svnstat: ");
172      svn_error_clear(err);
173      throw SVNException("commit_dates: svn_ra_get_latest_revnum failed");
174    }
175    // The struct we want to pass through to all log_message_receiver
176    // calls, here we only want to push all commit dates into a
177    // std::vector<std::string>.
178    struct log_receiver_baton lb;
179    lb.commit_dates.reserve(head.value.number+1); // revision 0 is also stored.
180    if ((err=svn_client_log3(apr_path, &peg, &start, &head, 0, false, true,
181                             log_message_receiver, static_cast<void*>(&lb),
182                             context_, pool_))) {
183      svn_handle_error2(err, stderr, false, "svnstat: ");
184      svn_error_clear(err);
185      throw SVNException("commit_dates: svn_client_log3 failed");
186    }
187    return lb.commit_dates;
188  }
189
190
191  void SVN::cleanup_failed_initialization(svn_error_t *err)
192  {
193    svn_handle_error2(err,stderr,false,"svnstat:");
194    svn_error_clear(err);
195    svn_pool_destroy(pool_);
196    apr_allocator_destroy(allocator_);
197  }
198
199
200  svn_error_t *
201  SVN::log_message_receiver(void *baton, apr_hash_t *changed_paths,
202                            svn_revnum_t rev, const char *author,
203                            const char *date, const char *msg, apr_pool_t *pool)
204  {
205    struct log_receiver_baton *lb=static_cast<struct log_receiver_baton*>(baton);
206    if (date && date[0])
207      lb->commit_dates.push_back(date);
208    else
209      throw SVNException("No date defined for revision: " + rev); 
210    return SVN_NO_ERROR;
211  }
212
213
214  void SVN::setup_ra_session(const std::string& path) {
215    // get a session to the repository
216    if (svn_error_t *err=svn_client_open_ra_session(&ra_session_, path.c_str(),
217                                                    context_,pool_)) {
218      svn_handle_error2(err,stderr,false,"svnstat:");
219      svn_error_clear(err);
220      throw SVNException("setup_ra_session: svn_client_open_ra_session failed");
221    }
222  }
223
224
225  void SVN::setup_wc_adm_access(const std::string& path)
226  {
227    // Set up svn administration area access. The whole structure is
228    // setup, maybe this is unnecessary?
229    const char* canonical_path=svn_path_internal_style(path.c_str(), pool_);
230    if (svn_error_t *err=svn_wc_adm_open3(&adm_access_, NULL, canonical_path,
231                                          false, -1, context_->cancel_func,
232                                          context_->cancel_baton, pool_)) {
233      svn_handle_error2(err,stderr,false,"svnstat:");
234      svn_error_clear(err);
235      throw SVNException("setup_wc_adm_access: svn_wc_adm_open3 failed");
236    }
237  }
238
239
240  SVN::vc_status SVN::version_controlled(const std::string& path)
241  {
242    svn_wc_status2_t* status=NULL;
243    if (svn_error_t *err=
244        svn_wc_status2(&status,svn_path_internal_style(path.c_str(), pool_),
245                       adm_access_, pool_)) {
246      svn_handle_error2(err,stderr,false,"svnstat:");
247      svn_error_clear(err);
248      throw SVNException("version_controlled(): svn_config_get_config failed");
249    }
250
251    if ((status->text_status==svn_wc_status_none) ||
252        (status->text_status==svn_wc_status_unversioned))
253      return unversioned;
254    else if (status->text_status==svn_wc_status_normal)
255      return uptodate;
256
257    return unresolved;
258  }
259
260
261}} // end of namespace svnstat and namespace theplu
Note: See TracBrowser for help on using the repository browser.