source: trunk/lib/SVN.cc @ 138

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

Fixes #23. No system calls to svn are done anymore.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 8.2 KB
Line 
1// $Id: SVN.cc 138 2006-08-03 21:40:15Z 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#include <apr_hash.h>
28#include <svn_cmdline.h>
29#include <svn_path.h>
30#include <svn_pools.h>
31#include <svn_wc.h>
32
33namespace theplu {
34namespace svnstat {
35
36
37  SVN* SVN::instance_=NULL;
38
39
40  SVN::SVN(void)
41    : adm_access_(NULL), allocator_(NULL), context_(NULL), pool_(NULL)
42  {
43    // To be honest, I (Jari) am not perfectly sure of what I am
44    // doing, most bits here are stolen from subversion source for the
45    // svn command line binary (subverion/svn/main.c)
46
47    svn_error_t* err=NULL;
48
49    // initialize something (APR subsystem and more). The APR
50    // subsystem is automatically destroyed at program exit. In case
51    // of several calls to svn_cmdline_init (ie. several exceptions
52    // thrown and caught with subsequent reinitializatios is safe
53    // memorywise but what about APR internal counters?)
54    if (svn_cmdline_init("svnstat",stderr) != EXIT_SUCCESS)
55      throw SVNException("SVN(void): svn_cmdline_init failed");
56
57    /// create top-level pool
58    if (apr_allocator_create(&allocator_))
59      throw SVNException("SVN(void): apr_allocator_create failed");
60    apr_allocator_max_free_set(allocator_,SVN_ALLOCATOR_RECOMMENDED_MAX_FREE);
61    pool_ = svn_pool_create_ex(NULL, allocator_);
62    apr_allocator_owner_set(allocator_, pool_);
63
64    // initialize the repository access library
65    if ((err=svn_ra_initialize(pool_))) {
66      // destroys pool and clears err
67      svn_cmdline_handle_exit_error(err, pool_, "svnstat: ");
68      apr_allocator_destroy(allocator_);
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      // destroys pool and clears err
78      svn_cmdline_handle_exit_error(err, pool_, "svnstat: ");
79      apr_allocator_destroy(allocator_);
80      throw SVNException("SVN(void): svn_config_ensure failed");
81    }
82
83    // create a client context object
84    if ((err=svn_client_create_context(&context_, pool_))) {
85      // destroys pool and clears err
86      svn_cmdline_handle_exit_error(err, pool_, "svnstat: ");
87      apr_allocator_destroy(allocator_);
88      throw SVNException("SVN(void): svn_client_create_context failed");
89    }
90
91    if ((err=svn_config_get_config(&(context_->config), NULL, pool_))) {
92      // destroys pool and clears err
93      svn_cmdline_handle_exit_error(err, pool_, "svnstat: ");
94      apr_allocator_destroy(allocator_);
95      throw SVNException("SVN(void): svn_config_get_config failed");
96    }
97
98    // set up authentication stuff
99    if ((err=svn_cmdline_setup_auth_baton(&(context_->auth_baton), false, NULL,
100                                          NULL, NULL, false,
101            static_cast<svn_config_t*>(apr_hash_get(context_->config,
102                                                    SVN_CONFIG_CATEGORY_CONFIG,
103                                                    APR_HASH_KEY_STRING)),
104                                          context_->cancel_func,
105                                          context_->cancel_baton, pool_))) {
106      svn_handle_error2(err, stderr, false, "svnstat: ");
107      svn_error_clear(err);
108      throw SVNException("SVN(void): svn_cmdline_setup_auth_baton failed");
109    }
110  }
111
112
113  SVN::~SVN(void)
114  {
115    svn_pool_destroy(pool_);
116    apr_allocator_destroy(allocator_);
117    // other apr resources acquired in svn_cmdline_init are destroyed
118    // at program exit, ok since SVN is a singleton
119    delete instance_;
120  }
121
122
123  svn_error_t* SVN::client_blame(const std::string& path,
124                                 svn_client_blame_receiver_t receiver,
125                                 void *baton)
126  {
127    // Setup to use all revisions
128    svn_opt_revision_t peg, start, head;
129    peg.kind=svn_opt_revision_unspecified;
130    start.kind=svn_opt_revision_number;
131    start.value.number=0;
132    head.kind=svn_opt_revision_head;
133    svn_error_t* err=svn_client_blame3(path.c_str(), &peg, &start, &head,
134                                       // Jari, below line creates memory leaks
135                                       svn_diff_file_options_create(pool_),
136                                       false, receiver, baton, context_, pool_);
137    if (err && err->apr_err!=SVN_ERR_CLIENT_IS_BINARY_FILE) {
138      svn_handle_error2(err, stderr, false, "svnstat: ");
139      svn_error_clear(err);
140      throw SVNException("SVN::client_blame: svn_client_blame3 failed");
141    }
142    return err;
143  }
144
145
146  std::vector<std::string> SVN::commit_dates(const std::string& path)
147  {
148    // Allocate space in pool_ for apr_path (here a string).
149    apr_array_header_t* apr_path=apr_array_make(pool_,1,4);
150    // Copy path to apr_path.
151    (*((const char **) apr_array_push(apr_path))) =
152      apr_pstrdup(pool_, svn_path_internal_style(path.c_str(),pool_));
153
154    // Setup to retrieve all commit logs.
155    svn_opt_revision_t peg, start, head;
156    peg.kind=svn_opt_revision_unspecified;
157    start.kind=svn_opt_revision_number;
158    start.value.number=0;
159    head.kind=svn_opt_revision_head;
160    svn_error_t* err=NULL;
161    // Retrieving the last revision is only needed for the reserve
162    // call below, not needed for the functionality here.
163    if ((err=svn_ra_get_latest_revnum(ra_session_,&(head.value.number),pool_))) {
164      svn_cmdline_handle_exit_error(err, pool_, "svnstat: ");
165      throw SVNException("commit_dates: svn_ra_get_latest_revnum failed");
166    }
167    // The struct we want to pass through to all log_message_receiver
168    // calls, here we only want to push all commit dates into a
169    // std::vector<std::string>.
170    struct log_receiver_baton lb;
171    lb.commit_dates.reserve(head.value.number+1); // revision 0 is also stored.
172    if ((err=svn_client_log3(apr_path, &peg, &start, &head, 0, false, true,
173                             log_message_receiver, static_cast<void*>(&lb),
174                             context_, pool_))) {
175      svn_cmdline_handle_exit_error(err, pool_, "svnstat: ");
176      throw SVNException("commit_dates: svn_client_log3 failed");
177    }
178    return lb.commit_dates;
179  }
180
181
182  svn_error_t *
183  SVN::log_message_receiver(void *baton, apr_hash_t *changed_paths,
184                            svn_revnum_t rev, const char *author,
185                            const char *date, const char *msg, apr_pool_t *pool)
186  {
187    struct log_receiver_baton *lb=static_cast<struct log_receiver_baton*>(baton);
188    if (date && date[0])
189      lb->commit_dates.push_back(date);
190    else
191      throw SVNException("No date defined for revision: " + rev); 
192    return SVN_NO_ERROR;
193  }
194
195
196  void SVN::setup_ra_session(const std::string& path) {
197    // get a session to the repository
198    if (svn_client_open_ra_session(&ra_session_, path.c_str(), context_,pool_)) {
199      // Jari, should the error space be cleared?
200      throw SVNException("setup_ra_session: svn_client_open_ra_session failed");
201    }
202  }
203
204
205  void SVN::setup_wc_adm_access(const std::string& path)
206  {
207    // Set up svn administration area access. The whole structure is
208    // setup, maybe this should not be done?
209    const char* canonical_path=svn_path_internal_style(path.c_str(), pool_);
210    if (svn_wc_adm_open3(&adm_access_, NULL, canonical_path, false, -1,
211                         context_->cancel_func,context_->cancel_baton, pool_))
212      // Jari, should the error space be cleared?
213      throw SVNException("setup_wc_adm_access: svn_wc_adm_open3 failed");
214  }
215
216
217  SVN::vc_status SVN::version_controlled(const std::string& path)
218  {
219    svn_wc_status2_t* status=NULL;
220    if (svn_wc_status2(&status,svn_path_internal_style(path.c_str(), pool_),
221                       adm_access_, pool_))
222      throw SVNException("version_controlled(): svn_config_get_config failed");
223
224    if ((status->text_status==svn_wc_status_none) ||
225        (status->text_status==svn_wc_status_unversioned))
226      return unversioned;
227    else if (status->text_status==svn_wc_status_normal)
228      return uptodate;
229
230    return unresolved;
231  }
232
233
234}} // end of namespace svnstat and namespace theplu
Note: See TracBrowser for help on using the repository browser.