2 ******************************************************************************
5 * @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010.
6 * Parts by Nokia Corporation (qt-info@nokia.com) Copyright (C) 2009.
8 * @see The GNU Public License (GPL) Version 3
12 *****************************************************************************/
14 * This program is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 3 of the License, or
17 * (at your option) any later version.
19 * This program is distributed in the hope that it will be useful, but
20 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
21 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
24 * You should have received a copy of the GNU General Public License along
25 * with this program; if not, write to the Free Software Foundation, Inc.,
26 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
29 #include "filesearch.h"
32 #include <QtCore/QIODevice>
33 #include <QtCore/QBuffer>
34 #include <QtCore/QFile>
35 #include <QtCore/QFutureInterface>
36 #include <QtConcurrent/QtConcurrentRun>
37 #include <QtCore/QRegExp>
38 #include <QtCore/QCoreApplication>
40 #include <qtconcurrent/runextensions.h>
42 using namespace Utils
;
44 static inline QString
msgCanceled(const QString
&searchTerm
, int numMatches
, int numFilesSearched
)
46 return QCoreApplication::translate("Utils::FileSearch",
47 "%1: canceled. %n occurrences found in %2 files.",
49 arg(searchTerm
).arg(numFilesSearched
);
52 static inline QString
msgFound(const QString
&searchTerm
, int numMatches
, int numFilesSearched
)
54 return QCoreApplication::translate("Utils::FileSearch",
55 "%1: %n occurrences found in %2 files.",
57 arg(searchTerm
).arg(numFilesSearched
);
60 static inline QString
msgFound(const QString
&searchTerm
, int numMatches
, int numFilesSearched
, int filesSize
)
62 return QCoreApplication::translate("Utils::FileSearch",
63 "%1: %n occurrences found in %2 of %3 files.",
65 arg(searchTerm
).arg(numFilesSearched
).arg(filesSize
);
69 void runFileSearch(QFutureInterface
<FileSearchResult
> &future
,
72 QTextDocument::FindFlags flags
,
73 QMap
<QString
, QString
> fileToContentsMap
)
75 future
.setProgressRange(0, files
.size());
76 int numFilesSearched
= 0;
79 bool caseInsensitive
= !(flags
& QTextDocument::FindCaseSensitively
);
80 bool wholeWord
= (flags
& QTextDocument::FindWholeWords
);
82 QByteArray sa
= searchTerm
.toUtf8();
83 int scMaxIndex
= sa
.length() - 1;
84 const char *sc
= sa
.constData();
86 QByteArray sal
= searchTerm
.toLower().toUtf8();
87 const char *scl
= sal
.constData();
89 QByteArray sau
= searchTerm
.toUpper().toUtf8();
90 const char *scu
= sau
.constData();
92 int chunkSize
= qMax(100000, sa
.length());
96 foreach(QString s
, files
) {
97 if (future
.isPaused()) {
98 future
.waitForResume();
100 if (future
.isCanceled()) {
101 future
.setProgressValueAndText(numFilesSearched
, msgCanceled(searchTerm
, numMatches
, numFilesSearched
));
105 if (fileToContentsMap
.contains(s
)) {
106 buffer
.setData(fileToContentsMap
.value(s
).toLocal8Bit());
112 if (!device
->open(QIODevice::ReadOnly
)) {
116 const char *startOfLastLine
= NULL
;
118 bool firstChunk
= true;
119 while (!device
->atEnd()) {
121 device
->seek(device
->pos() - sa
.length() + 1);
124 const QByteArray chunk
= device
->read(chunkSize
);
125 const char *chunkPtr
= chunk
.constData();
126 startOfLastLine
= chunkPtr
;
127 for (const char *regionPtr
= chunkPtr
; regionPtr
< chunkPtr
+ chunk
.length() - scMaxIndex
; ++regionPtr
) {
128 const char *regionEnd
= regionPtr
+ scMaxIndex
;
130 if (*regionPtr
== '\n') {
131 startOfLastLine
= regionPtr
+ 1;
135 (!caseInsensitive
&& *regionPtr
== sc
[0] && *regionEnd
== sc
[scMaxIndex
])
138 (caseInsensitive
&& (*regionPtr
== scl
[0] || *regionPtr
== scu
[0])
139 && (*regionEnd
== scl
[scMaxIndex
] || *regionEnd
== scu
[scMaxIndex
]))
141 const char *afterRegion
= regionEnd
+ 1;
142 const char *beforeRegion
= regionPtr
- 1;
145 (isalnum(*beforeRegion
)
146 || (*beforeRegion
== '_')
147 || isalnum(*afterRegion
)
148 || (*afterRegion
== '_'))) {
153 for (const char *regionCursor
= regionPtr
+ 1; regionCursor
< regionEnd
; ++regionCursor
, ++regionIndex
) {
154 if ( // case sensitive
155 (!caseInsensitive
&& equal
&& *regionCursor
!= sc
[regionIndex
])
158 (caseInsensitive
&& equal
&& *regionCursor
!= sc
[regionIndex
] && *regionCursor
!= scl
[regionIndex
] && *regionCursor
!= scu
[regionIndex
])
164 int textLength
= chunk
.length() - (startOfLastLine
- chunkPtr
);
165 if (textLength
> 0) {
170 while (startOfLastLine
[i
] != '\n' && startOfLastLine
[i
] != '\r' && i
< textLength
&& n
++ < 256) {
171 res
.append(startOfLastLine
[i
++]);
173 future
.reportResult(FileSearchResult(s
, lineNr
, QString(res
),
174 regionPtr
- startOfLastLine
, sa
.length()));
183 future
.setProgressValueAndText(numFilesSearched
, msgFound(searchTerm
, numMatches
, numFilesSearched
, files
.size()));
186 if (!future
.isCanceled()) {
187 future
.setProgressValueAndText(numFilesSearched
, msgFound(searchTerm
, numMatches
, numFilesSearched
));
191 void runFileSearchRegExp(QFutureInterface
<FileSearchResult
> &future
,
194 QTextDocument::FindFlags flags
,
195 QMap
<QString
, QString
> fileToContentsMap
)
197 future
.setProgressRange(0, files
.size());
198 int numFilesSearched
= 0;
200 if (flags
& QTextDocument::FindWholeWords
) {
201 searchTerm
= QString::fromLatin1("\\b%1\\b").arg(searchTerm
);
203 const Qt::CaseSensitivity caseSensitivity
= (flags
& QTextDocument::FindCaseSensitively
) ? Qt::CaseSensitive
: Qt::CaseInsensitive
;
204 const QRegExp
expression(searchTerm
, caseSensitivity
);
209 foreach(const QString
&s
, files
) {
210 if (future
.isPaused()) {
211 future
.waitForResume();
213 if (future
.isCanceled()) {
214 future
.setProgressValueAndText(numFilesSearched
, msgCanceled(searchTerm
, numMatches
, numFilesSearched
));
218 bool needsToCloseFile
= false;
219 if (fileToContentsMap
.contains(s
)) {
220 str
= fileToContentsMap
.value(s
);
221 stream
.setString(&str
);
224 if (!file
.open(QIODevice::ReadOnly
)) {
227 needsToCloseFile
= true;
228 stream
.setDevice(&file
);
232 while (!stream
.atEnd()) {
233 line
= stream
.readLine();
235 while ((pos
= expression
.indexIn(line
, pos
)) != -1) {
236 future
.reportResult(FileSearchResult(s
, lineNr
, line
,
237 pos
, expression
.matchedLength()));
238 pos
+= expression
.matchedLength();
243 future
.setProgressValueAndText(numFilesSearched
, msgFound(searchTerm
, numMatches
, numFilesSearched
, files
.size()));
244 if (needsToCloseFile
) {
248 if (!future
.isCanceled()) {
249 future
.setProgressValueAndText(numFilesSearched
, msgFound(searchTerm
, numMatches
, numFilesSearched
));
255 QFuture
<FileSearchResult
> Utils::findInFiles(const QString
&searchTerm
, const QStringList
&files
,
256 QTextDocument::FindFlags flags
, QMap
<QString
, QString
> fileToContentsMap
)
258 return QtConcurrent::run
<FileSearchResult
, QString
, QStringList
, QTextDocument::FindFlags
, QMap
<QString
, QString
> >
259 (runFileSearch
, searchTerm
, files
, flags
, fileToContentsMap
);
262 QFuture
<FileSearchResult
> Utils::findInFilesRegExp(const QString
&searchTerm
, const QStringList
&files
,
263 QTextDocument::FindFlags flags
, QMap
<QString
, QString
> fileToContentsMap
)
265 return QtConcurrent::run
<FileSearchResult
, QString
, QStringList
, QTextDocument::FindFlags
, QMap
<QString
, QString
> >
266 (runFileSearchRegExp
, searchTerm
, files
, flags
, fileToContentsMap
);