File indexing completed on 2024-04-28 05:42:08

0001 /*
0002  * Port for usage with qt-framework and development for kdesvn
0003  * Copyright (C) 2005-2009 by Rajko Albrecht (ral@alwins-world.de)
0004  * https://kde.org/applications/development/org.kde.kdesvn
0005  */
0006 /*
0007  * ====================================================================
0008  * Copyright (c) 2002-2005 The RapidSvn Group.  All rights reserved.
0009  * dev@rapidsvn.tigris.org
0010  *
0011  * This library is free software; you can redistribute it and/or
0012  * modify it under the terms of the GNU Lesser General Public
0013  * License as published by the Free Software Foundation; either
0014  * version 2.1 of the License, or (at your option) any later version.
0015  *
0016  * This library is distributed in the hope that it will be useful,
0017  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0018  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0019  * Lesser General Public License for more details.
0020  *
0021  * You should have received a copy of the GNU Lesser General Public
0022  * License along with this library (in the file LGPL.txt); if not,
0023  * write to the Free Software Foundation, Inc., 51 Franklin St,
0024  * Fifth Floor, Boston, MA  02110-1301  USA
0025  *
0026  * This software consists of voluntary contributions made by many
0027  * individuals.  For exact contribution history, see the revision
0028  * history and logs, available at http://rapidsvn.tigris.org/.
0029  * ====================================================================
0030  */
0031 
0032 // svncpp
0033 #include "client_impl.h"
0034 #include "helper.h"
0035 
0036 // Subversion api
0037 #include <svn_client.h>
0038 #include <svn_path.h>
0039 #include <svn_sorts.h>
0040 
0041 #include "client_parameter.h"
0042 #include "context_listener.h"
0043 #include "dirent.h"
0044 #include "exception.h"
0045 #include "info_entry.h"
0046 #include "pool.h"
0047 #include "status.h"
0048 #include "svnqt_defines.h"
0049 #include "targets.h"
0050 #include "url.h"
0051 #include <QCoreApplication>
0052 
0053 namespace svn
0054 {
0055 
0056 struct LogBaton {
0057     ContextWP context;
0058     LogEntriesMap *logEntries;
0059     QList<qlonglong> *revstack;
0060     StringArray excludeList;
0061     LogBaton()
0062         : logEntries(nullptr)
0063         , revstack(nullptr)
0064     {
0065     }
0066 };
0067 
0068 struct StatusEntriesBaton {
0069     StatusEntries entries;
0070     apr_pool_t *pool;
0071     ContextWP m_Context;
0072     StatusEntriesBaton()
0073         : entries()
0074         , pool(nullptr)
0075     {
0076     }
0077 };
0078 
0079 struct InfoEntriesBaton {
0080     InfoEntries entries;
0081     apr_pool_t *pool;
0082     ContextWP m_Context;
0083     InfoEntriesBaton()
0084         : entries()
0085         , pool(nullptr)
0086     {
0087     }
0088 };
0089 
0090 static svn_error_t *logMapReceiver2(void *baton, svn_log_entry_t *log_entry, apr_pool_t *pool)
0091 {
0092     Q_UNUSED(pool);
0093     LogBaton *l_baton = static_cast<LogBaton *>(baton);
0094     ContextP l_context = l_baton->context;
0095     if (l_context.isNull()) {
0096         return SVN_NO_ERROR;
0097     }
0098     svn_client_ctx_t *ctx = l_context->ctx();
0099     if (ctx && ctx->cancel_func) {
0100         SVN_ERR(ctx->cancel_func(ctx->cancel_baton));
0101     }
0102     QList<qlonglong> *rstack = l_baton->revstack;
0103     if (!SVN_IS_VALID_REVNUM(log_entry->revision)) {
0104         if (rstack && !rstack->isEmpty()) {
0105             rstack->pop_front();
0106         }
0107         return SVN_NO_ERROR;
0108     }
0109     LogEntriesMap *entries = l_baton->logEntries;
0110     (*entries)[log_entry->revision] = LogEntry(log_entry, l_baton->excludeList);
0111     /// @TODO insert it into last logentry
0112     if (rstack) {
0113         (*entries)[log_entry->revision].m_MergedInRevisions = (*rstack);
0114         if (log_entry->has_children) {
0115             rstack->push_front(log_entry->revision);
0116         }
0117     }
0118     return SVN_NO_ERROR;
0119 }
0120 
0121 static svn_error_t *InfoEntryFunc(void *baton, const char *path, const svn_client_info2_t *info, apr_pool_t *)
0122 {
0123     InfoEntriesBaton *seb = static_cast<InfoEntriesBaton *>(baton);
0124     if (seb->m_Context) {
0125         /* check every loop for cancel of operation */
0126         ContextP l_context = seb->m_Context;
0127         if (!l_context) {
0128             return svn_error_create(SVN_ERR_CANCELLED, nullptr, QCoreApplication::translate("svnqt", "Cancelled by user.").toUtf8());
0129         }
0130         svn_client_ctx_t *ctx = l_context->ctx();
0131         if (ctx && ctx->cancel_func) {
0132             SVN_ERR(ctx->cancel_func(ctx->cancel_baton));
0133         }
0134     }
0135     seb->entries.push_back(InfoEntry(info, path));
0136     return nullptr;
0137 }
0138 
0139 static svn_error_t *StatusEntriesFunc(void *baton, const char *path, const svn_client_status_t *status, apr_pool_t *pool)
0140 {
0141     // use own pool - the parameter will cleared between loops!
0142     Q_UNUSED(pool);
0143     StatusEntriesBaton *seb = static_cast<StatusEntriesBaton *>(baton);
0144     if (seb->m_Context) {
0145         /* check every loop for cancel of operation */
0146         ContextP l_context = seb->m_Context;
0147         if (!l_context) {
0148             return svn_error_create(SVN_ERR_CANCELLED, nullptr, QCoreApplication::translate("svnqt", "Cancelled by user.").toUtf8());
0149         }
0150         svn_client_ctx_t *ctx = l_context->ctx();
0151         if (ctx && ctx->cancel_func) {
0152             SVN_ERR(ctx->cancel_func(ctx->cancel_baton));
0153         }
0154     }
0155 
0156     seb->entries.push_back(StatusPtr(new Status(path, status)));
0157     return nullptr;
0158 }
0159 
0160 static StatusEntries localStatus(const StatusParameter &params, const ContextP &context)
0161 {
0162     svn_error_t *error;
0163     svn_revnum_t revnum;
0164     Revision rev(Revision::HEAD);
0165     Pool pool;
0166     StatusEntriesBaton baton;
0167 
0168     baton.pool = pool;
0169 
0170     error = svn_client_status5(&revnum,
0171                                *context,
0172                                params.path().path().toUtf8(),
0173                                rev,
0174                                internal::DepthToSvn(params.depth()), // see svn::Depth
0175                                params.all(), // get all not only interesting
0176                                params.update(), // check for updates
0177                                params.noIgnore(), // hide ignored files or not
0178                                params.ignoreExternals(), // hide external
0179                                true, // depth as sticky  - TODO
0180                                params.changeList().array(pool),
0181                                StatusEntriesFunc,
0182                                &baton,
0183                                pool);
0184 
0185     Client_impl::checkErrorThrow(error);
0186     return baton.entries;
0187 }
0188 
0189 static StatusPtr dirEntryToStatus(const Path &path, const DirEntry &dirEntry)
0190 {
0191     QString url = path.path() + QLatin1Char('/') + dirEntry.name();
0192     return StatusPtr(new Status(url, dirEntry));
0193 }
0194 
0195 static StatusPtr infoEntryToStatus(const Path &, const InfoEntry &infoEntry)
0196 {
0197     return StatusPtr(new Status(infoEntry.url().toString(), infoEntry));
0198 }
0199 
0200 static StatusEntries remoteStatus(Client *client, const StatusParameter &params, const ContextP &)
0201 {
0202     const DirEntries dirEntries = client->list(params.path(), params.revision(), params.revision(), params.depth(), params.detailedRemote());
0203 
0204     StatusEntries entries;
0205     for (const DirEntry &dirEntry : dirEntries) {
0206         if (dirEntry.name().isEmpty()) {
0207             continue;
0208         }
0209         entries.push_back(dirEntryToStatus(params.path(), dirEntry));
0210     }
0211     return entries;
0212 }
0213 
0214 StatusEntries Client_impl::status(const StatusParameter &params)
0215 {
0216     if (Url::isValid(params.path().path())) {
0217         return remoteStatus(this, params, m_context);
0218     }
0219     return localStatus(params, m_context);
0220 }
0221 
0222 static StatusPtr localSingleStatus(const Path &path, const ContextP &context, bool update = false)
0223 {
0224     svn_error_t *error;
0225     Pool pool;
0226     StatusEntriesBaton baton;
0227     svn_revnum_t revnum;
0228     Revision rev(Revision::HEAD);
0229 
0230     baton.pool = pool;
0231 
0232     error = svn_client_status5(&revnum,
0233                                *context,
0234                                path.path().toUtf8(),
0235                                rev,
0236                                svn_depth_empty, // not recurse
0237                                true, // get all not only interesting
0238                                update, // check for updates
0239                                false, // hide ignored files or not
0240                                false, // hide external
0241                                true, // depth as sticky
0242                                nullptr,
0243                                StatusEntriesFunc,
0244                                &baton,
0245                                pool);
0246 
0247     Client_impl::checkErrorThrow(error);
0248     if (baton.entries.isEmpty()) {
0249         return StatusPtr(new Status());
0250     }
0251 
0252     return baton.entries.at(0);
0253 }
0254 
0255 static StatusPtr remoteSingleStatus(Client *client, const Path &path, const Revision &revision, const ContextP &)
0256 {
0257     const InfoEntries infoEntries = client->info(path, DepthEmpty, revision, Revision(Revision::UNDEFINED));
0258     if (infoEntries.isEmpty()) {
0259         return StatusPtr(new Status());
0260     }
0261     return infoEntryToStatus(path, infoEntries.at(0));
0262 }
0263 
0264 StatusPtr Client_impl::singleStatus(const Path &path, bool update, const Revision &revision)
0265 {
0266     if (Url::isValid(path.path())) {
0267         return remoteSingleStatus(this, path, revision, m_context);
0268     }
0269     return localSingleStatus(path, m_context, update);
0270 }
0271 
0272 bool Client_impl::log(const LogParameter &params, LogEntriesMap &log_target)
0273 {
0274     Pool pool;
0275     LogBaton l_baton;
0276     QList<qlonglong> revstack;
0277     l_baton.context = m_context;
0278     l_baton.excludeList = params.excludeList();
0279     l_baton.logEntries = &log_target;
0280     l_baton.revstack = &revstack;
0281     svn_error_t *error;
0282 
0283     error = svn_client_log5(params.targets().array(pool),
0284                             params.peg().revision(),
0285                             svn::internal::RevisionRangesToHash(params.revisions()).array(pool),
0286                             params.limit(),
0287                             params.discoverChangedPathes() ? 1 : 0,
0288                             params.strictNodeHistory() ? 1 : 0,
0289                             params.includeMergedRevisions() ? 1 : 0,
0290                             params.revisionProperties().array(pool),
0291                             logMapReceiver2,
0292                             &l_baton,
0293                             *m_context, // client ctx
0294                             pool);
0295     checkErrorThrow(error);
0296     return true;
0297 }
0298 
0299 InfoEntries Client_impl::info(const Path &_p, Depth depth, const Revision &rev, const Revision &peg_revision, const StringArray &changelists)
0300 {
0301     Pool pool;
0302     svn_error_t *error = nullptr;
0303     InfoEntriesBaton baton;
0304 
0305     baton.pool = pool;
0306     baton.m_Context = m_context;
0307     svn_opt_revision_t pegr;
0308     const char *truepath = nullptr;
0309     bool internal_peg = false;
0310     QByteArray _buf = _p.cstr();
0311 
0312     error = svn_opt_parse_path(&pegr, &truepath, _buf, pool);
0313     checkErrorThrow(error);
0314     if (!truepath) {
0315         throw ClientException("no path given!");
0316     }
0317     if (peg_revision.kind() == svn_opt_revision_unspecified) {
0318         if ((svn_path_is_url(_p.cstr())) && (pegr.kind == svn_opt_revision_unspecified)) {
0319             pegr.kind = svn_opt_revision_head;
0320             internal_peg = true;
0321         }
0322     }
0323 
0324     error = svn_client_info3(truepath,
0325                              internal_peg ? &pegr : peg_revision.revision(),
0326                              rev.revision(),
0327                              internal::DepthToSvn(depth),
0328                              false, // TODO parameter for fetch exclueded
0329                              false, // TODO parameter for fetch_actual_only
0330                              changelists.array(pool),
0331                              &InfoEntryFunc,
0332                              &baton,
0333                              *m_context,
0334                              pool);
0335 
0336     checkErrorThrow(error);
0337     return baton.entries;
0338 }
0339 }
0340 
0341 /* -----------------------------------------------------------------
0342  * local variables:
0343  * eval: (load-file "../../rapidsvn-dev.el")
0344  * end:
0345  */