diff --git a/kdevplatform/util/environmentprofilelist.cpp b/kdevplatform/util/environmentprofilelist.cpp index 353a28413f..72963c971a 100644 --- a/kdevplatform/util/environmentprofilelist.cpp +++ b/kdevplatform/util/environmentprofilelist.cpp @@ -1,309 +1,309 @@ /* This file is part of KDevelop Copyright 2007 Andreas Pakulat This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "environmentprofilelist.h" #include "kdevstringhandler.h" #include "debug.h" #include #include #include #include #include namespace KDevelop { class EnvironmentProfileListPrivate { public: QMap> m_profiles; QString m_defaultProfileName; }; } using namespace KDevelop; namespace { namespace Strings { // TODO: migrate to more consistent key term "Default Environment Profile" inline QString defaultEnvProfileKey() { return QStringLiteral("Default Environment Group"); } inline QString envGroup() { return QStringLiteral("Environment Settings"); } // TODO: migrate to more consistent key term "Profile List" inline QString profileListKey() { return QStringLiteral("Group List"); } inline QString defaultProfileName() { return QStringLiteral("default"); } } void decode(KConfig* config, EnvironmentProfileListPrivate* d) { KConfigGroup cfg(config, Strings::envGroup()); d->m_defaultProfileName = cfg.readEntry(Strings::defaultEnvProfileKey(), Strings::defaultProfileName()); const QStringList profileNames = cfg.readEntry(Strings::profileListKey(), QStringList{Strings::defaultProfileName()}); for (const auto& profileName : profileNames) { KConfigGroup envgrp(&cfg, profileName); QMap variables; const auto varNames = envgrp.keyList(); for (const QString& varname : varNames) { variables[varname] = envgrp.readEntry(varname, QString()); } d->m_profiles.insert(profileName, variables); } } void encode(KConfig* config, const EnvironmentProfileListPrivate* d) { KConfigGroup cfg(config, Strings::envGroup()); cfg.writeEntry(Strings::defaultEnvProfileKey(), d->m_defaultProfileName); cfg.writeEntry(Strings::profileListKey(), d->m_profiles.keys()); const auto oldGroupList = cfg.groupList(); for (const QString& group : oldGroupList) { if (!d->m_profiles.contains(group)) { cfg.deleteGroup(group); } } for (auto it = d->m_profiles.cbegin(), itEnd = d->m_profiles.cend(); it != itEnd; ++it) { KConfigGroup envgrp(&cfg, it.key()); envgrp.deleteGroup(); const auto val = it.value(); for (auto it2 = val.cbegin(), it2End = val.cend(); it2 != it2End; ++it2) { envgrp.writeEntry(it2.key(), *it2); } } cfg.sync(); } } EnvironmentProfileList::EnvironmentProfileList(const EnvironmentProfileList& rhs) : d_ptr(new EnvironmentProfileListPrivate(*rhs.d_ptr)) { } EnvironmentProfileList& EnvironmentProfileList::operator=(const EnvironmentProfileList& rhs) { Q_D(EnvironmentProfileList); *d = *rhs.d_ptr; return *this; } EnvironmentProfileList::EnvironmentProfileList(const KSharedConfigPtr& config) : d_ptr(new EnvironmentProfileListPrivate) { Q_D(EnvironmentProfileList); decode(config.data(), d); } EnvironmentProfileList::EnvironmentProfileList(KConfig* config) : d_ptr(new EnvironmentProfileListPrivate) { Q_D(EnvironmentProfileList); decode(config, d); } EnvironmentProfileList::~EnvironmentProfileList() = default; QMap EnvironmentProfileList::variables(const QString& profileName) const { Q_D(const EnvironmentProfileList); return d->m_profiles.value(profileName.isEmpty() ? d->m_defaultProfileName : profileName); } QMap& EnvironmentProfileList::variables(const QString& profileName) { Q_D(EnvironmentProfileList); return d->m_profiles[profileName.isEmpty() ? d->m_defaultProfileName : profileName]; } QString EnvironmentProfileList::defaultProfileName() const { Q_D(const EnvironmentProfileList); return d->m_defaultProfileName; } void EnvironmentProfileList::setDefaultProfile(const QString& profileName) { Q_D(EnvironmentProfileList); if (profileName.isEmpty() || !d->m_profiles.contains(profileName)) { return; } d->m_defaultProfileName = profileName; } void EnvironmentProfileList::saveSettings(KConfig* config) const { Q_D(const EnvironmentProfileList); encode(config, d); config->sync(); } void EnvironmentProfileList::loadSettings(KConfig* config) { Q_D(EnvironmentProfileList); d->m_profiles.clear(); decode(config, d); } QStringList EnvironmentProfileList::profileNames() const { Q_D(const EnvironmentProfileList); return d->m_profiles.keys(); } void EnvironmentProfileList::removeProfile(const QString& profileName) { Q_D(EnvironmentProfileList); d->m_profiles.remove(profileName); } EnvironmentProfileList::EnvironmentProfileList() : d_ptr(new EnvironmentProfileListPrivate) { } QStringList EnvironmentProfileList::createEnvironment(const QString& profileName, const QStringList& defaultEnvironment) const { QMap retMap; for (const QString& line : defaultEnvironment) { QString varName = line.section(QLatin1Char('='), 0, 0); QString varValue = line.section(QLatin1Char('='), 1); retMap.insert(varName, varValue); } if (!profileName.isEmpty()) { const auto userMap = variables(profileName); for (QMap::const_iterator it = userMap.constBegin(); it != userMap.constEnd(); ++it) { retMap.insert(it.key(), it.value()); } } QStringList env; env.reserve(retMap.size()); for (QMap::const_iterator it = retMap.constBegin(); it != retMap.constEnd(); ++it) { env << it.key() + QLatin1Char('=') + it.value(); } return env; } static QString expandVariable(const QString &key, const QString &value, QMap &output, const QMap &input, const QProcessEnvironment &environment) { if (value.isEmpty()) return QString(); auto it = output.constFind(key); if (it != output.constEnd()) { // nothing to do, value was expanded already return *it; } // not yet expanded, do that now auto variableValue = [&](const QString &variable) { if (environment.contains(variable)) { return environment.value(variable); } else if (variable == key) { qCWarning(UTIL) << "recursive variable expansion" << variable; return QString(); } else if (input.contains(variable)) { return expandVariable(variable, input.value(variable), output, input, environment); } else { qCWarning(UTIL) << "Couldn't find replacement for" << variable; return QString(); } }; constexpr ushort escapeChar{'\\'}; constexpr ushort variableStartChar{'$'}; - const auto isSpecialSymbol = [](QChar c) { + const auto isSpecialSymbol = [=](QChar c) { return c.unicode() == escapeChar || c.unicode() == variableStartChar; }; auto& expanded = output[key]; expanded.reserve(value.size()); const int lastIndex = value.size() - 1; int i = 0; // Never treat value.back() as a special symbol (nothing to escape or start). while (i < lastIndex) { const auto currentChar = value[i]; switch (currentChar.unicode()) { case escapeChar: { const auto nextChar = value[i+1]; if (!isSpecialSymbol(nextChar)) { expanded += currentChar; // Nothing to escape => keep the escapeChar. } expanded += nextChar; i += 2; break; } case variableStartChar: { ++i; const auto match = matchPossiblyBracedAsciiVariable(value.midRef(i)); if (match.length == 0) { expanded += currentChar; // Not a variable name start. } else { expanded += variableValue(match.name); i += match.length; } break; } default: expanded += currentChar; ++i; } } if (i == lastIndex) { expanded += value[i]; } return expanded; } void KDevelop::expandVariables(QMap& variables, const QProcessEnvironment& environment) { QMap expanded; for (auto it = variables.cbegin(), end = variables.cend(); it != end; ++it) { expandVariable(it.key(), it.value(), expanded, variables, environment); } variables = expanded; } diff --git a/kdevplatform/util/kdevstringhandler.cpp b/kdevplatform/util/kdevstringhandler.cpp index bc155d821e..8b5b9d279c 100644 --- a/kdevplatform/util/kdevstringhandler.cpp +++ b/kdevplatform/util/kdevstringhandler.cpp @@ -1,294 +1,294 @@ /* This file is part of KDevelop Copyright 2009 Andreas Pakulat This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. This file mostly code takes from Qt's QSettings class, the copyright header from that file follows: **************************************************************************** ** ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). ** Contact: Nokia Corporation (qt-info@nokia.com) ** ** This file is part of the QtCore module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial Usage ** Licensees holding valid Qt Commercial licenses may use this file in ** accordance with the Qt Commercial License Agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Nokia. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain ** additional rights. These rights are described in the Nokia Qt LGPL ** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this ** package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** If you are unsure which license is appropriate for your use, please ** contact the sales department at http://www.qtsoftware.com/contact. ** $QT_END_LICENSE$ ** **************************************************************************** */ #include "kdevstringhandler.h" #include #include #include #include #include #include #include #include #include #include #include namespace KDevelop { QString joinWithEscaping(const QStringList& input, QChar joinchar, QChar escapechar) { QStringList tmp = input; return tmp.replaceInStrings(joinchar, QString(joinchar) + QString(escapechar)).join(joinchar); } QStringList splitWithEscaping(const QString& input, QChar splitchar, QChar escapechar) { enum State { Normal, SeenEscape } state; state = Normal; QStringList result; QString currentstring; for (const QChar c : input) { switch (state) { case Normal: if (c == escapechar) { state = SeenEscape; } else if (c == splitchar) { result << currentstring; currentstring.clear(); } else { currentstring += c; } break; case SeenEscape: currentstring += c; state = Normal; break; } } if (!currentstring.isEmpty()) { result << currentstring; } return result; } QVariant stringToQVariant(const QString& s) { // Taken from qsettings.cpp, stringToVariant() if (s.startsWith(QLatin1Char('@'))) { if (s.endsWith(QLatin1Char(')'))) { if (s.startsWith(QLatin1String("@Variant("))) { QByteArray a(s.toLatin1().mid(9)); QDataStream stream(&a, QIODevice::ReadOnly); stream.setVersion(QDataStream::Qt_4_4); QVariant result; stream >> result; return result; } } } return QVariant(); } QString qvariantToString(const QVariant& variant) { // Taken from qsettings.cpp, variantToString() QByteArray a; { QDataStream s(&a, QIODevice::WriteOnly); s.setVersion(QDataStream::Qt_4_4); s << variant; } QString result = QLatin1String("@Variant(") + QString::fromLatin1(a.constData(), a.size()) + QLatin1Char(')'); return result; } QString htmlToPlainText(const QString& s, HtmlToPlainTextMode mode) { switch (mode) { case FastMode: { QString result(s); result.remove(QRegExp(QStringLiteral("<[^>]+>"))); return result; } case CompleteMode: { QTextDocument doc; doc.setHtml(s); return doc.toPlainText(); } } return QString(); // never reached } } int KDevelop::findAsciiIdentifierLength(const QStringRef& str) { if (str.isEmpty()) { return 0; } constexpr ushort maxAscii{127}; const auto firstChar = str[0].unicode(); const bool isIdentifier = firstChar <= maxAscii && (std::isalpha(firstChar) || firstChar == '_'); if (!isIdentifier) { return 0; } - const auto partOfIdentifier = [](QChar character) { + const auto partOfIdentifier = [=](QChar character) { const auto u = character.unicode(); return u <= maxAscii && (std::isalnum(u) || u == '_'); }; return std::find_if_not(str.cbegin() + 1, str.cend(), partOfIdentifier) - str.cbegin(); } KDevelop::VariableMatch KDevelop::matchPossiblyBracedAsciiVariable(const QStringRef& str) { if (str.isEmpty()) { return {}; } if (str[0].unicode() == '{') { const auto nameLength = findAsciiIdentifierLength(str.mid(1)); if (nameLength == 0) { return {}; } const auto closingBraceIndex = 1 + nameLength; if (closingBraceIndex < str.size() && str[closingBraceIndex].unicode() == '}') { return {nameLength + 2, str.mid(1, nameLength).toString()}; } } else { const auto nameLength = findAsciiIdentifierLength(str); if (nameLength != 0) { return {nameLength, str.left(nameLength).toString()}; } } return {}; } QString KDevelop::stripAnsiSequences(const QString& str) { if (str.isEmpty()) { return QString(); // fast path } enum { PLAIN, ANSI_START, ANSI_CSI, ANSI_SEQUENCE, ANSI_WAITING_FOR_ST, ANSI_ST_STARTED } state = PLAIN; QString result; result.reserve(str.count()); for (const QChar c : str) { const auto val = c.unicode(); switch (state) { case PLAIN: if (val == 27) // 'ESC' state = ANSI_START; else if (val == 155) // equivalent to 'ESC'-'[' state = ANSI_CSI; else result.append(c); break; case ANSI_START: if (val == 91) // [ state = ANSI_CSI; else if (val == 80 || val == 93 || val == 94 || val == 95) // 'P', ']', '^' and '_' state = ANSI_WAITING_FOR_ST; else if (val >= 64 && val <= 95) state = PLAIN; else state = ANSI_SEQUENCE; break; case ANSI_CSI: if (val >= 64 && val <= 126) // Anything between '@' and '~' state = PLAIN; break; case ANSI_SEQUENCE: if (val >= 64 && val <= 95) // Anything between '@' and '_' state = PLAIN; break; case ANSI_WAITING_FOR_ST: if (val == 7) // 'BEL' state = PLAIN; else if (val == 27) // 'ESC' state = ANSI_ST_STARTED; break; case ANSI_ST_STARTED: if (val == 92) // '\' state = PLAIN; else state = ANSI_WAITING_FOR_ST; break; } } return result; } void KDevelop::normalizeLineEndings(QByteArray& text) { for (int i = 0, s = text.size(); i < s; ++i) { if (text[i] != '\r') { continue; } if (i + 1 < s && text[i + 1] == '\n') { text.remove(i, 1); } else { text[i] = '\n'; } } }