File indexing completed on 2024-12-01 05:11:50
0001 // clang-format off 0002 /* 0003 class CvsIgnoreList from Cervisia cvsdir.cpp 0004 SPDX-FileCopyrightText: 1999-2002 Bernd Gehrmann <bernd at mail.berlios.de> 0005 with elements from class StringMatcher 0006 SPDX-FileCopyrightText: 2003 Andre Woebbeking <Woebbeking at web.de> 0007 Modifications for KDiff3 by Joachim Eibl 0008 0009 SPDX-FileCopyrightText: 2002-2011 Joachim Eibl, joachim.eibl at gmx.de 0010 SPDX-FileCopyrightText: 2018-2020 Michael Reeves reeves.87@gmail.com 0011 SPDX-License-Identifier: GPL-2.0-or-later 0012 */ 0013 // clang-format on 0014 #include "CvsIgnoreList.h" 0015 0016 #include "fileaccess.h" // for FileAccess 0017 0018 #include <list> 0019 #include <utility> // for pair 0020 0021 #include <QByteArray> 0022 #include <QDir> 0023 #include <QFile> 0024 #include <QRegularExpression> 0025 #include <QStringList> 0026 #include <QTextStream> 0027 0028 CvsIgnoreList::CvsIgnoreList() = default; 0029 0030 CvsIgnoreList::~CvsIgnoreList() = default; 0031 0032 void CvsIgnoreList::enterDir(const QString& dir, const DirectoryList& directoryList) 0033 { 0034 static const QString ignorestr = QString::fromLatin1(". .. core RCSLOG tags TAGS RCS SCCS .make.state " 0035 ".nse_depinfo #* .#* cvslog.* ,* CVS CVS.adm .del-* *.a *.olb *.o *.obj " 0036 "*.so *.Z *~ *.old *.elc *.ln *.bak *.BAK *.orig *.rej *.exe _$* *$"); 0037 addEntriesFromString(dir, ignorestr); 0038 addEntriesFromFile(dir, QDir::homePath() + '/' + getGlobalIgnoreName()); 0039 const char* varname = getVarName(); 0040 if(qEnvironmentVariableIsSet(varname) && !qEnvironmentVariableIsEmpty(varname)) 0041 { 0042 addEntriesFromString(dir, QString::fromLocal8Bit(qgetenv(varname))); 0043 } 0044 const bool bUseLocalCvsIgnore = ignoreExists(directoryList); 0045 if(bUseLocalCvsIgnore) 0046 { 0047 FileAccess file(dir); 0048 file.addPath(getIgnoreName()); 0049 if(file.exists() && file.isLocal()) 0050 { 0051 addEntriesFromFile(dir, file.absoluteFilePath()); 0052 } 0053 else 0054 { 0055 file.createLocalCopy(); 0056 addEntriesFromFile(dir, file.getTempName()); 0057 } 0058 } 0059 } 0060 0061 void CvsIgnoreList::addEntriesFromString(const QString& dir, const QString& str) 0062 { 0063 const QStringList patternList = str.split(' '); 0064 for(const QString& pattern: patternList) 0065 { 0066 addEntry(dir, pattern); 0067 } 0068 } 0069 0070 /* 0071 We don't have a real file in AUTOTEST mode 0072 */ 0073 void CvsIgnoreList::addEntriesFromFile(const QString& dir, const QString& name) 0074 { //want unused warning when not building autotest 0075 #ifdef AUTOTEST 0076 Q_UNUSED(name); 0077 Q_UNUSED(dir); 0078 #else 0079 QFile file(name); 0080 0081 if(file.open(QIODevice::ReadOnly)) 0082 { 0083 QTextStream stream(&file); 0084 while(!stream.atEnd()) 0085 { 0086 addEntry(dir, stream.readLine()); 0087 } 0088 } 0089 #endif 0090 } 0091 0092 void CvsIgnoreList::addEntry(const QString& dir, const QString& pattern) 0093 { 0094 if(pattern != QString("!")) 0095 { 0096 if(pattern.isEmpty()) return; 0097 0098 // The general match is general but slow. 0099 // Special tests for '*' and '?' at the beginning or end of a pattern 0100 // allow fast checks. 0101 0102 // Count number of '*' and '?' 0103 quint32 nofMetaCharacters = 0; 0104 0105 const QChar* pos; 0106 pos = pattern.unicode(); 0107 const QChar* posEnd; 0108 posEnd = pos + pattern.length(); 0109 while(pos < posEnd) 0110 { 0111 if(*pos == QChar('*') || *pos == QChar('?')) ++nofMetaCharacters; 0112 ++pos; 0113 } 0114 0115 if(nofMetaCharacters == 0) 0116 { 0117 m_ignorePatterns[dir].m_exactPatterns.append(pattern); 0118 } 0119 else if(nofMetaCharacters == 1) 0120 { 0121 if(pattern.at(0) == QChar('*')) 0122 { 0123 m_ignorePatterns[dir].m_endPatterns.append(pattern.right(pattern.length() - 1)); 0124 } 0125 else if(pattern.at(pattern.length() - 1) == QChar('*')) 0126 { 0127 m_ignorePatterns[dir].m_startPatterns.append(pattern.left(pattern.length() - 1)); 0128 } 0129 else 0130 { 0131 m_ignorePatterns[dir].m_generalPatterns.append(pattern); 0132 } 0133 } 0134 else 0135 { 0136 m_ignorePatterns[dir].m_generalPatterns.append(pattern); 0137 } 0138 } 0139 else 0140 { 0141 m_ignorePatterns.erase(dir); 0142 } 0143 } 0144 0145 bool CvsIgnoreList::matches(const QString& dir, const QString& text, bool bCaseSensitive) const 0146 { 0147 const auto ignorePatternsIt = m_ignorePatterns.find(dir); 0148 if(ignorePatternsIt == m_ignorePatterns.end()) 0149 { 0150 return false; 0151 } 0152 //Do not use QStringList::indexof here it has no case flag 0153 if(ignorePatternsIt->second.m_exactPatterns.contains(text, bCaseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive)) 0154 { 0155 return true; 0156 } 0157 0158 for(const QString& startPattern: ignorePatternsIt->second.m_startPatterns) 0159 { 0160 if(text.startsWith(startPattern, bCaseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive)) 0161 { 0162 return true; 0163 } 0164 } 0165 0166 for(const QString& endPattern: ignorePatternsIt->second.m_endPatterns) 0167 { 0168 if(text.endsWith(endPattern, bCaseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive)) 0169 { 0170 return true; 0171 } 0172 } 0173 0174 for(const QString& globStr: ignorePatternsIt->second.m_generalPatterns) 0175 { 0176 QRegularExpression pattern(QRegularExpression::wildcardToRegularExpression(globStr), bCaseSensitive ? QRegularExpression::UseUnicodePropertiesOption : QRegularExpression::UseUnicodePropertiesOption | QRegularExpression::CaseInsensitiveOption); 0177 if(pattern.match(text).hasMatch()) 0178 return true; 0179 } 0180 0181 return false; 0182 } 0183 0184 bool CvsIgnoreList::ignoreExists(const DirectoryList& pDirList) 0185 { 0186 for(const FileAccess& dir: pDirList) 0187 { 0188 if(dir.fileName() == getIgnoreName()) 0189 return true; 0190 } 0191 return false; 0192 }