File indexing completed on 2024-05-05 04:41:00
0001 /* 0002 SPDX-FileCopyrightText: 2007 Andreas Pakulat <apaku@gmx.de> 0003 0004 Parts of the file are copied from the RapidSvn C++ library 0005 SPDX-FileCopyrightText: 2002-2006 The RapidSvn Group 0006 0007 SPDX-License-Identifier: LGPL-2.0-or-later 0008 */ 0009 0010 #include "svnclient.h" 0011 0012 #include <QDateTime> 0013 #include <QStandardPaths> 0014 0015 extern "C" { 0016 #include <svn_client.h> 0017 #include <svn_io.h> 0018 } 0019 #include "kdevsvncpp/targets.hpp" 0020 #include "kdevsvncpp/pool.hpp" 0021 0022 #include <vcs/vcsrevision.h> 0023 #include <vcs/vcsannotation.h> 0024 0025 void fail (apr_pool_t *pool, apr_status_t status, const char *fmt, ...) 0026 { 0027 va_list ap; 0028 char *msg; 0029 svn_error_t * error; 0030 0031 va_start (ap, fmt); 0032 msg = apr_pvsprintf (pool, fmt, ap); 0033 va_end (ap); 0034 0035 error = svn_error_create (status, nullptr, msg); 0036 throw svn::ClientException (error); 0037 } 0038 0039 void cleanup( apr_file_t* outfile, const char* outfileName, apr_file_t* errfile, const char* errfileName, const svn::Pool& pool ) 0040 { 0041 if( outfile != nullptr ) 0042 { 0043 apr_file_close( outfile ); 0044 } 0045 0046 if( errfile != nullptr ) 0047 { 0048 apr_file_close( outfile ); 0049 } 0050 0051 if( outfileName != nullptr ) 0052 { 0053 svn_error_clear( svn_io_remove_file ( outfileName, pool ) ); 0054 } 0055 0056 0057 if( errfileName != nullptr ) 0058 { 0059 svn_error_clear( svn_io_remove_file ( errfileName, pool ) ); 0060 } 0061 0062 } 0063 0064 SvnClient::SvnClient( svn::Context* ctx ) 0065 : QObject(nullptr), svn::Client( ctx ), m_ctxt( ctx ) 0066 { 0067 } 0068 0069 QString SvnClient::diff( const svn::Path& src, const svn::Revision& srcRev, 0070 const svn::Path& dst, const svn::Revision& dstRev, 0071 const bool recurse, const bool ignoreAncestry, 0072 const bool noDiffDeleted, const bool ignoreContentType ) 0073 { 0074 svn::Pool pool; 0075 // null options 0076 apr_array_header_t *options = svn_cstring_split( "", "\t\r\n", false, pool ); 0077 0078 svn_error_t* error; 0079 0080 const char* outfileName = nullptr; 0081 apr_file_t* outfile = nullptr; 0082 const char* errfileName = nullptr; 0083 apr_file_t* errfile = nullptr; 0084 0085 QByteArray ba = QString(QStandardPaths::writableLocation(QStandardPaths::TempLocation)+QLatin1String("/kdevelop_svn_diff")).toUtf8(); 0086 0087 error = svn_io_open_unique_file( &outfile, &outfileName, ba.data(), ".tmp", false, pool ); 0088 0089 if( error != nullptr ) 0090 { 0091 ::cleanup( outfile, outfileName, errfile, errfileName, pool ); 0092 throw svn::ClientException( error ); 0093 } 0094 0095 error = svn_io_open_unique_file( &errfile, &errfileName, ba.data(), ".tmp", false, pool ); 0096 0097 if( error != nullptr ) 0098 { 0099 ::cleanup( outfile, outfileName, errfile, errfileName, pool ); 0100 throw svn::ClientException( error ); 0101 } 0102 0103 error = svn_client_diff3( options, 0104 src.c_str(), srcRev.revision(), 0105 dst.c_str(), dstRev.revision(), 0106 recurse, ignoreAncestry, noDiffDeleted, 0107 ignoreContentType, "UTF-8", 0108 outfile, errfile, m_ctxt->ctx(), pool ); 0109 if ( error ) 0110 { 0111 ::cleanup( outfile, outfileName, errfile, errfileName, pool ); 0112 throw svn::ClientException(error); 0113 } 0114 0115 // then we reopen outfile for reading 0116 apr_status_t aprstatus = apr_file_close (outfile); 0117 if (aprstatus) 0118 { 0119 ::cleanup (outfile, outfileName, errfile, errfileName, pool); 0120 ::fail (pool, aprstatus, "failed to close '%s'", outfileName); 0121 } 0122 0123 aprstatus = apr_file_open (&outfile, outfileName, APR_READ, APR_OS_DEFAULT, pool); 0124 if (aprstatus) 0125 { 0126 ::cleanup (outfile, outfileName, errfile, errfileName, pool); 0127 ::fail (pool, aprstatus, "failed to open '%s'", outfileName); 0128 } 0129 0130 0131 svn_stringbuf_t* stringbuf; 0132 // now we can read the diff output from outfile and return that 0133 error = svn_stringbuf_from_aprfile (&stringbuf, outfile, pool); 0134 0135 if (error != nullptr) 0136 { 0137 ::cleanup (outfile, outfileName, errfile, errfileName, pool); 0138 throw svn::ClientException (error); 0139 } 0140 0141 ::cleanup (outfile, outfileName, errfile, errfileName, pool); 0142 return QString::fromUtf8( stringbuf->data ); 0143 } 0144 0145 QString SvnClient::diff( const svn::Path& src, const svn::Revision& pegRev, 0146 const svn::Revision& srcRev, const svn::Revision& dstRev, 0147 const bool recurse, const bool ignoreAncestry, 0148 const bool noDiffDeleted, const bool ignoreContentType ) 0149 { 0150 svn::Pool pool; 0151 // null options 0152 apr_array_header_t *options = svn_cstring_split( "", "\t\r\n", false, pool ); 0153 0154 0155 svn_error_t* error; 0156 0157 const char* outfileName = nullptr; 0158 apr_file_t* outfile = nullptr; 0159 const char* errfileName = nullptr; 0160 apr_file_t* errfile = nullptr; 0161 0162 QByteArray ba = QStandardPaths::writableLocation(QStandardPaths::TempLocation).toUtf8(); 0163 0164 error = svn_io_open_unique_file( &outfile, &outfileName, ba.data(), ".tmp", false, pool ); 0165 0166 if( error != nullptr ) 0167 { 0168 ::cleanup( outfile, outfileName, errfile, errfileName, pool ); 0169 throw svn::ClientException( error ); 0170 } 0171 0172 error = svn_io_open_unique_file( &errfile, &errfileName, ba.data(), ".tmp", false, pool ); 0173 0174 if( error != nullptr ) 0175 { 0176 ::cleanup( outfile, outfileName, errfile, errfileName, pool ); 0177 throw svn::ClientException( error ); 0178 } 0179 0180 error = svn_client_diff_peg3( options, 0181 src.c_str(), pegRev.revision(), 0182 srcRev.revision(), dstRev.revision(), 0183 recurse, ignoreAncestry, noDiffDeleted, 0184 ignoreContentType, "UTF-8", 0185 outfile, errfile, m_ctxt->ctx(), pool ); 0186 if ( error ) 0187 { 0188 ::cleanup( outfile, outfileName, errfile, errfileName, pool ); 0189 throw svn::ClientException(error); 0190 } 0191 0192 // then we reopen outfile for reading 0193 apr_status_t aprstatus = apr_file_close (outfile); 0194 if (aprstatus) 0195 { 0196 ::cleanup (outfile, outfileName, errfile, errfileName, pool); 0197 ::fail (pool, aprstatus, "failed to close '%s'", outfileName); 0198 } 0199 0200 aprstatus = apr_file_open (&outfile, outfileName, APR_READ, APR_OS_DEFAULT, pool); 0201 if (aprstatus) 0202 { 0203 ::cleanup (outfile, outfileName, errfile, errfileName, pool); 0204 ::fail (pool, aprstatus, "failed to open '%s'", outfileName); 0205 } 0206 0207 0208 svn_stringbuf_t* stringbuf; 0209 // now we can read the diff output from outfile and return that 0210 error = svn_stringbuf_from_aprfile (&stringbuf, outfile, pool); 0211 0212 if (error != nullptr) 0213 { 0214 ::cleanup (outfile, outfileName, errfile, errfileName, pool); 0215 throw svn::ClientException(error); 0216 } 0217 0218 ::cleanup (outfile, outfileName, errfile, errfileName, pool); 0219 return QString::fromUtf8( stringbuf->data ); 0220 } 0221 0222 static svn_error_t * 0223 kdev_logReceiver (void *baton, 0224 apr_hash_t * changedPaths, 0225 svn_revnum_t rev, 0226 const char *author, 0227 const char *date, 0228 const char *msg, 0229 apr_pool_t * pool) 0230 { 0231 auto* client = (SvnClient *) baton; 0232 0233 KDevelop::VcsEvent ev; 0234 ev.setAuthor( QString::fromUtf8( author ) ); 0235 ev.setDate( QDateTime::fromString( QString::fromUtf8( date ), Qt::ISODate ) ); 0236 ev.setMessage( QString::fromUtf8( msg ) ); 0237 KDevelop::VcsRevision vcsrev; 0238 vcsrev.setRevisionValue( QVariant( qlonglong( rev ) ), KDevelop::VcsRevision::GlobalNumber ); 0239 ev.setRevision( vcsrev ); 0240 0241 if (changedPaths != nullptr) 0242 { 0243 for (apr_hash_index_t *hi = apr_hash_first (pool, changedPaths); 0244 hi != nullptr; 0245 hi = apr_hash_next (hi)) 0246 { 0247 char *path; 0248 void *val; 0249 apr_hash_this (hi, (const void **)&path, nullptr, &val); 0250 0251 auto *log_item = reinterpret_cast<svn_log_changed_path_t *> (val); 0252 KDevelop::VcsItemEvent iev; 0253 iev.setRepositoryLocation( QString::fromUtf8( path ) ); 0254 iev.setRepositoryCopySourceLocation( QString::fromUtf8( log_item->copyfrom_path ) ); 0255 KDevelop::VcsRevision irev; 0256 irev.setRevisionValue( QVariant( qlonglong( log_item->copyfrom_rev ) ), 0257 KDevelop::VcsRevision::GlobalNumber ); 0258 iev.setRepositoryCopySourceRevision( irev ); 0259 switch( log_item->action ) 0260 { 0261 case 'A': 0262 iev.setActions( KDevelop::VcsItemEvent::Added ); 0263 break; 0264 case 'M': 0265 iev.setActions( KDevelop::VcsItemEvent::Modified ); 0266 break; 0267 case 'D': 0268 iev.setActions( KDevelop::VcsItemEvent::Deleted ); 0269 break; 0270 case 'R': 0271 iev.setActions( KDevelop::VcsItemEvent::Replaced ); 0272 break; 0273 } 0274 0275 auto items = ev.items(); 0276 items.append( iev ); 0277 ev.setItems( items ); 0278 } 0279 } 0280 client->emitLogEventReceived( ev ); 0281 0282 return nullptr; 0283 } 0284 0285 void SvnClient::log( const char* path, 0286 const svn::Revision& start, 0287 const svn::Revision& end, 0288 int limit, 0289 bool discoverChangedPaths, 0290 bool strictNodeHistory ) 0291 { 0292 svn::Pool pool; 0293 svn::Targets target(path); 0294 svn_error_t *error; 0295 0296 error = svn_client_log2 ( 0297 target.array(pool), 0298 start.revision(), 0299 end.revision(), 0300 limit, 0301 discoverChangedPaths ? 1 : 0, 0302 strictNodeHistory ? 1 : 0, 0303 kdev_logReceiver, 0304 this, 0305 m_ctxt->ctx(), // client ctx 0306 pool); 0307 0308 if (error != nullptr) 0309 { 0310 throw svn::ClientException (error); 0311 } 0312 } 0313 0314 void SvnClient::emitLogEventReceived( const KDevelop::VcsEvent& ev ) 0315 { 0316 emit logEventReceived( ev ); 0317 } 0318 0319 #include "moc_svnclient.cpp"