source: trunk/tools/qdoc3/config.cpp@ 651

Last change on this file since 651 was 651, checked in by Dmitry A. Kuminov, 15 years ago

trunk: Merged in qt 4.6.2 sources.

File size: 26.2 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation (qt-info@nokia.com)
6**
7** This file is part of the tools applications of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
15**
16** GNU Lesser General Public License Usage
17** Alternatively, this file may be used under the terms of the GNU Lesser
18** General Public License version 2.1 as published by the Free Software
19** Foundation and appearing in the file LICENSE.LGPL included in the
20** packaging of this file. Please review the following information to
21** ensure the GNU Lesser General Public License version 2.1 requirements
22** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23**
24** In addition, as a special exception, Nokia gives you certain additional
25** rights. These rights are described in the Nokia Qt LGPL Exception
26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you have questions regarding the use of this file, please contact
37** Nokia at qt-info@nokia.com.
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42/*
43 config.cpp
44*/
45
46#include <QtCore>
47
48#include "archiveextractor.h"
49#include "config.h"
50#include "uncompressor.h"
51#include <stdlib.h>
52
53QT_BEGIN_NAMESPACE
54
55/*
56 An entry on the MetaStack.
57 */
58class MetaStackEntry
59{
60public:
61 void open();
62 void close();
63
64 QStringList accum;
65 QStringList next;
66};
67
68/*
69 */
70void MetaStackEntry::open()
71{
72 next.append(QString());
73}
74
75/*
76 */
77void MetaStackEntry::close()
78{
79 accum += next;
80 next.clear();
81}
82
83/*
84 ###
85*/
86class MetaStack : private QStack<MetaStackEntry>
87{
88public:
89 MetaStack();
90
91 void process(QChar ch, const Location& location);
92 QStringList getExpanded(const Location& location);
93};
94
95MetaStack::MetaStack()
96{
97 push(MetaStackEntry());
98 top().open();
99}
100
101void MetaStack::process(QChar ch, const Location& location)
102{
103 if (ch == QLatin1Char('{')) {
104 push(MetaStackEntry());
105 top().open();
106 }
107 else if (ch == QLatin1Char('}')) {
108 if (count() == 1)
109 location.fatal(tr("Unexpected '}'"));
110
111 top().close();
112 QStringList suffixes = pop().accum;
113 QStringList prefixes = top().next;
114
115 top().next.clear();
116 QStringList::ConstIterator pre = prefixes.begin();
117 while (pre != prefixes.end()) {
118 QStringList::ConstIterator suf = suffixes.begin();
119 while (suf != suffixes.end()) {
120 top().next << (*pre + *suf);
121 ++suf;
122 }
123 ++pre;
124 }
125 }
126 else if (ch == QLatin1Char(',') && count() > 1) {
127 top().close();
128 top().open();
129 }
130 else {
131 QStringList::Iterator pre = top().next.begin();
132 while (pre != top().next.end()) {
133 *pre += ch;
134 ++pre;
135 }
136 }
137}
138
139QStringList MetaStack::getExpanded(const Location& location)
140{
141 if (count() > 1)
142 location.fatal(tr("Missing '}'"));
143
144 top().close();
145 return top().accum;
146}
147
148QT_STATIC_CONST_IMPL QString Config::dot = QLatin1String(".");
149QMap<QString, QString> Config::uncompressedFiles;
150QMap<QString, QString> Config::extractedDirs;
151int Config::numInstances;
152
153/*!
154 \class Config
155 \brief The Config class contains the configuration variables
156 for controlling how qdoc produces documentation.
157
158 Its load() function, reads, parses, and processes a qdocconf file.
159 */
160
161/*!
162 The constructor sets the \a programName and initializes all
163 internal state variables to empty values.
164 */
165Config::Config(const QString& programName)
166 : prog(programName)
167{
168 loc = Location::null;
169 lastLoc = Location::null;
170 locMap.clear();
171 stringValueMap.clear();
172 stringListValueMap.clear();
173 numInstances++;
174}
175
176/*!
177 The destructor deletes all the temporary files and
178 directories it built.
179 */
180Config::~Config()
181{
182 if (--numInstances == 0) {
183 QMap<QString, QString>::ConstIterator f = uncompressedFiles.begin();
184 while (f != uncompressedFiles.end()) {
185 QDir().remove(*f);
186 ++f;
187 }
188 uncompressedFiles.clear();
189
190 QMap<QString, QString>::ConstIterator d = extractedDirs.begin();
191 while (d != extractedDirs.end()) {
192 removeDirContents(*d);
193 QDir dir(*d);
194 QString name = dir.dirName();
195 dir.cdUp();
196 dir.rmdir(name);
197 ++d;
198 }
199 extractedDirs.clear();
200 }
201}
202
203/*!
204 Loads and parses the qdoc configuration file \a fileName.
205 This function calls the other load() function, which does
206 the loading, parsing, and processing of the configuration
207 file.
208
209 Intializes the location variables returned by location()
210 and lastLocation().
211 */
212void Config::load(const QString& fileName)
213{
214 load(Location::null, fileName);
215 if (loc.isEmpty()) {
216 loc = Location(fileName);
217 }
218 else {
219 loc.setEtc(true);
220 }
221 lastLoc = Location::null;
222}
223
224/*!
225 Joins all the strings in \a values into a single string with the
226 individual \a values separated by ' '. Then it inserts the result
227 into the string list map with \a var as the key.
228
229 It also inserts the \a values string list into a separate map,
230 also with \a var as the key.
231 */
232void Config::setStringList(const QString& var, const QStringList& values)
233{
234 stringValueMap[var] = values.join(QLatin1String(" "));
235 stringListValueMap[var] = values;
236}
237
238/*!
239 Looks up the configuarion variable \a var in the string
240 map and returns the boolean value.
241 */
242bool Config::getBool(const QString& var) const
243{
244 return QVariant(getString(var)).toBool();
245}
246
247/*!
248 Looks up the configuration variable \a var in the string list
249 map. Iterates through the string list found, interpreting each
250 string in the list as an integer and adding it to a total sum.
251 Returns the sum.
252 */
253int Config::getInt(const QString& var) const
254{
255 QStringList strs = getStringList(var);
256 QStringList::ConstIterator s = strs.begin();
257 int sum = 0;
258
259 while (s != strs.end()) {
260 sum += (*s).toInt();
261 ++s;
262 }
263 return sum;
264}
265
266/*!
267 First, this function looks up the configuration variable \a var
268 in the location map and, if found, sets the internal variable
269 \c{lastLoc} to the Location that \a var maps to.
270
271 Then it looks up the configuration variable \a var in the string
272 map, and returns the string that \a var maps to.
273 */
274QString Config::getString(const QString& var) const
275{
276 if (!locMap[var].isEmpty())
277 (Location&) lastLoc = locMap[var];
278 return stringValueMap[var];
279}
280
281/*!
282 Looks up the configuration variable \a var in the string
283 list map, converts the string list it maps to into a set
284 of strings, and returns the set.
285 */
286QSet<QString> Config::getStringSet(const QString& var) const
287{
288 return QSet<QString>::fromList(getStringList(var));
289}
290
291/*!
292 First, this function looks up the configuration variable \a var
293 in the location map and, if found, sets the internal variable
294 \c{lastLoc} the Location that \a var maps to.
295
296 Then it looks up the configuration variable \a var in the string
297 list map, and returns the string list that \a var maps to.
298 */
299QStringList Config::getStringList(const QString& var) const
300{
301 if (!locMap[var].isEmpty())
302 (Location&) lastLoc = locMap[var];
303 return stringListValueMap[var];
304}
305
306/*!
307 Calls getRegExpList() with the control variable \a var and
308 iterates through the resulting list of regular expressions,
309 concatening them with some extras characters to form a single
310 QRegExp, which is returned/
311
312 \sa getRegExpList()
313 */
314QRegExp Config::getRegExp(const QString& var) const
315{
316 QString pattern;
317 QList<QRegExp> subRegExps = getRegExpList(var);
318 QList<QRegExp>::ConstIterator s = subRegExps.begin();
319
320 while (s != subRegExps.end()) {
321 if (!(*s).isValid())
322 return *s;
323 if (!pattern.isEmpty())
324 pattern += QLatin1Char('|');
325 pattern += QLatin1String("(?:") + (*s).pattern() + QLatin1Char(')');
326 ++s;
327 }
328 if (pattern.isEmpty())
329 pattern = QLatin1String("$x"); // cannot match
330 return QRegExp(pattern);
331}
332
333/*!
334 Looks up the configuration variable \a var in the string list
335 map, converts the string list to a list of regular expressions,
336 and returns it.
337 */
338QList<QRegExp> Config::getRegExpList(const QString& var) const
339{
340 QStringList strs = getStringList(var);
341 QStringList::ConstIterator s = strs.begin();
342 QList<QRegExp> regExps;
343
344 while (s != strs.end()) {
345 regExps += QRegExp(*s);
346 ++s;
347 }
348 return regExps;
349}
350
351/*!
352 This function is slower than it could be.
353 */
354QSet<QString> Config::subVars(const QString& var) const
355{
356 QSet<QString> result;
357 QString varDot = var + QLatin1Char('.');
358 QMap<QString, QString>::ConstIterator v = stringValueMap.begin();
359 while (v != stringValueMap.end()) {
360 if (v.key().startsWith(varDot)) {
361 QString subVar = v.key().mid(varDot.length());
362 int dot = subVar.indexOf(QLatin1Char('.'));
363 if (dot != -1)
364 subVar.truncate(dot);
365 result.insert(subVar);
366 }
367 ++v;
368 }
369 return result;
370}
371
372/*!
373 Builds and returns a list of file pathnames for the file
374 type specified by \a filesVar (e.g. "headers" or "sources").
375 The files are found in the directories specified by
376 \a dirsVar, and they are filtered by \a defaultNameFilter
377 if a better filter can't be constructed from \a filesVar.
378 The directories in \a excludedDirs are avoided.
379 */
380QStringList Config::getAllFiles(const QString &filesVar,
381 const QString &dirsVar,
382 const QString &defaultNameFilter,
383 const QSet<QString> &excludedDirs)
384{
385 QStringList result = getStringList(filesVar);
386 QStringList dirs = getStringList(dirsVar);
387
388 QString nameFilter = getString(filesVar + dot +
389 QLatin1String(CONFIG_FILEEXTENSIONS));
390 if (nameFilter.isEmpty())
391 nameFilter = defaultNameFilter;
392
393 QStringList::ConstIterator d = dirs.begin();
394 while (d != dirs.end()) {
395 result += getFilesHere(*d, nameFilter, excludedDirs);
396 ++d;
397 }
398 return result;
399}
400
401/*!
402 \a fileName is the path of the file to find.
403
404 \a files and \a dirs are the lists where we must find the
405 components of \a fileName.
406
407 \a location is used for obtaining the file and line numbers
408 for report qdoc errors.
409 */
410QString Config::findFile(const Location& location,
411 const QStringList& files,
412 const QStringList& dirs,
413 const QString& fileName,
414 QString& userFriendlyFilePath)
415{
416 if (fileName.isEmpty() || fileName.startsWith(QLatin1Char('/'))) {
417 userFriendlyFilePath = fileName;
418 return fileName;
419 }
420
421 QFileInfo fileInfo;
422 QStringList components = fileName.split(QLatin1Char('?'));
423 QString firstComponent = components.first();
424
425 QStringList::ConstIterator f = files.begin();
426 while (f != files.end()) {
427 if (*f == firstComponent ||
428 (*f).endsWith(QLatin1Char('/') + firstComponent)) {
429 fileInfo.setFile(*f);
430 if (!fileInfo.exists())
431 location.fatal(tr("File '%1' does not exist").arg(*f));
432 break;
433 }
434 ++f;
435 }
436
437 if (fileInfo.fileName().isEmpty()) {
438 QStringList::ConstIterator d = dirs.begin();
439 while (d != dirs.end()) {
440 fileInfo.setFile(QDir(*d), firstComponent);
441 if (fileInfo.exists()) {
442 break;
443 }
444 ++d;
445 }
446 }
447
448 userFriendlyFilePath = QString();
449 if (!fileInfo.exists())
450 return QString();
451
452 QStringList::ConstIterator c = components.begin();
453 for (;;) {
454 bool isArchive = (c != components.end() - 1);
455 ArchiveExtractor *extractor = 0;
456 QString userFriendly = *c;
457
458 if (isArchive) {
459 extractor = ArchiveExtractor::extractorForFileName(userFriendly);
460 }
461
462 if (extractor == 0) {
463 Uncompressor *uncompressor =
464 Uncompressor::uncompressorForFileName(userFriendly);
465 if (uncompressor != 0) {
466 QString fileNameWithCorrectExtension =
467 uncompressor->uncompressedFilePath(
468 fileInfo.filePath());
469 QString uncompressed = uncompressedFiles[fileInfo.filePath()];
470 if (uncompressed.isEmpty()) {
471 uncompressed =
472 QTemporaryFile(fileInfo.filePath()).fileName();
473 uncompressor->uncompressFile(location,
474 fileInfo.filePath(),
475 uncompressed);
476 uncompressedFiles[fileInfo.filePath()] = uncompressed;
477 }
478 fileInfo.setFile(uncompressed);
479
480 if (isArchive) {
481 extractor = ArchiveExtractor::extractorForFileName(
482 fileNameWithCorrectExtension);
483 }
484 else {
485 userFriendly = fileNameWithCorrectExtension;
486 }
487 }
488 }
489 userFriendlyFilePath += userFriendly;
490
491 if (isArchive) {
492 if (extractor == 0)
493 location.fatal(tr("Unknown archive type '%1'")
494 .arg(userFriendlyFilePath));
495 QString extracted = extractedDirs[fileInfo.filePath()];
496 if (extracted.isEmpty()) {
497 extracted = QTemporaryFile(fileInfo.filePath()).fileName();
498 if (!QDir().mkdir(extracted))
499 location.fatal(tr("Cannot create temporary directory '%1'")
500 .arg(extracted));
501 extractor->extractArchive(location, fileInfo.filePath(),
502 extracted);
503 extractedDirs[fileInfo.filePath()] = extracted;
504 }
505 ++c;
506 fileInfo.setFile(QDir(extracted), *c);
507 }
508 else {
509 break;
510 }
511 userFriendlyFilePath += "?";
512 }
513 return fileInfo.filePath();
514}
515
516/*!
517 */
518QString Config::findFile(const Location& location,
519 const QStringList& files,
520 const QStringList& dirs,
521 const QString& fileBase,
522 const QStringList& fileExtensions,
523 QString& userFriendlyFilePath)
524{
525 QStringList::ConstIterator e = fileExtensions.begin();
526 while (e != fileExtensions.end()) {
527 QString filePath = findFile(location, files, dirs, fileBase + "." + *e,
528 userFriendlyFilePath);
529 if (!filePath.isEmpty())
530 return filePath;
531 ++e;
532 }
533 return findFile(location, files, dirs, fileBase, userFriendlyFilePath);
534}
535
536/*!
537 Copies the \a sourceFilePath to the file name constructed by
538 concatenating \a targetDirPath and \a userFriendlySourceFilePath.
539 \a location is for identifying the file and line number where
540 a qdoc error occurred. The constructed output file name is
541 returned.
542 */
543QString Config::copyFile(const Location& location,
544 const QString& sourceFilePath,
545 const QString& userFriendlySourceFilePath,
546 const QString& targetDirPath)
547{
548 QFile inFile(sourceFilePath);
549 if (!inFile.open(QFile::ReadOnly)) {
550 location.fatal(tr("Cannot open input file '%1': %2")
551 .arg(inFile.fileName()).arg(inFile.errorString()));
552 return "";
553 }
554
555 QString outFileName = userFriendlySourceFilePath;
556 int slash = outFileName.lastIndexOf("/");
557 if (slash != -1)
558 outFileName = outFileName.mid(slash);
559
560 QFile outFile(targetDirPath + "/" + outFileName);
561 if (!outFile.open(QFile::WriteOnly)) {
562 location.fatal(tr("Cannot open output file '%1': %2")
563 .arg(outFile.fileName()).arg(outFile.errorString()));
564 return "";
565 }
566
567 char buffer[1024];
568 int len;
569 while ((len = inFile.read(buffer, sizeof(buffer))) > 0) {
570 outFile.write(buffer, len);
571 }
572 return outFileName;
573}
574
575/*!
576 Finds the largest unicode digit in \a value in the range
577 1..7 and returns it.
578 */
579int Config::numParams(const QString& value)
580{
581 int max = 0;
582 for (int i = 0; i != value.length(); i++) {
583 uint c = value[i].unicode();
584 if (c > 0 && c < 8)
585 max = qMax(max, (int)c);
586 }
587 return max;
588}
589
590/*!
591 Removes everything from \a dir. This function is recursive.
592 It doesn't remove \a dir itself, but if it was called
593 recursively, then the caller will remove \a dir.
594 */
595bool Config::removeDirContents(const QString& dir)
596{
597 QDir dirInfo(dir);
598 QFileInfoList entries = dirInfo.entryInfoList();
599
600 bool ok = true;
601
602 QFileInfoList::Iterator it = entries.begin();
603 while (it != entries.end()) {
604 if ((*it).isFile()) {
605 if (!dirInfo.remove((*it).fileName()))
606 ok = false;
607 }
608 else if ((*it).isDir()) {
609 if ((*it).fileName() != "." && (*it).fileName() != "..") {
610 if (removeDirContents((*it).absoluteFilePath())) {
611 if (!dirInfo.rmdir((*it).fileName()))
612 ok = false;
613 }
614 else {
615 ok = false;
616 }
617 }
618 }
619 ++it;
620 }
621 return ok;
622}
623
624/*!
625 Returns true if \a ch is a letter, number, '_', '.',
626 '{', '}', or ','.
627 */
628bool Config::isMetaKeyChar(QChar ch)
629{
630 return ch.isLetterOrNumber()
631 || ch == QLatin1Char('_')
632 || ch == QLatin1Char('.')
633 || ch == QLatin1Char('{')
634 || ch == QLatin1Char('}')
635 || ch == QLatin1Char(',');
636}
637
638/*!
639 Load, parse, and process a qdoc configuration file. This
640 function is only called by the other load() function, but
641 this one is recursive, i.e., it calls itself when it sees
642 an \c{include} statement in the qdog configuration file.
643 */
644void Config::load(Location location, const QString& fileName)
645{
646 QRegExp keySyntax("\\w+(?:\\.\\w+)*");
647
648#define SKIP_CHAR() \
649 do { \
650 location.advance(c); \
651 ++i; \
652 c = text.at(i); \
653 cc = c.unicode(); \
654 } while (0)
655
656#define SKIP_SPACES() \
657 while (c.isSpace() && cc != '\n') \
658 SKIP_CHAR()
659
660#define PUT_CHAR() \
661 word += c; \
662 SKIP_CHAR();
663
664 if (location.depth() > 16)
665 location.fatal(tr("Too many nested includes"));
666
667 QFile fin(fileName);
668 if (!fin.open(QFile::ReadOnly | QFile::Text)) {
669 fin.setFileName(fileName + ".qdoc");
670 if (!fin.open(QFile::ReadOnly | QFile::Text))
671 location.fatal(tr("Cannot open file '%1': %2").arg(fileName).arg(fin.errorString()));
672 }
673
674 QString text = fin.readAll();
675 text += QLatin1String("\n\n");
676 text += QChar('\0');
677 fin.close();
678
679 location.push(fileName);
680 location.start();
681
682 int i = 0;
683 QChar c = text.at(0);
684 uint cc = c.unicode();
685 while (i < (int) text.length()) {
686 if (cc == 0)
687 ++i;
688 else if (c.isSpace()) {
689 SKIP_CHAR();
690 }
691 else if (cc == '#') {
692 do {
693 SKIP_CHAR();
694 } while (cc != '\n');
695 }
696 else if (isMetaKeyChar(c)) {
697 Location keyLoc = location;
698 bool plus = false;
699 QString stringValue;
700 QStringList stringListValue;
701 QString word;
702 bool inQuote = false;
703 bool prevWordQuoted = true;
704 bool metWord = false;
705
706 MetaStack stack;
707 do {
708 stack.process(c, location);
709 SKIP_CHAR();
710 } while (isMetaKeyChar(c));
711
712 QStringList keys = stack.getExpanded(location);
713 SKIP_SPACES();
714
715 if (keys.count() == 1 && keys.first() == "include") {
716 QString includeFile;
717
718 if (cc != '(')
719 location.fatal(tr("Bad include syntax"));
720 SKIP_CHAR();
721 SKIP_SPACES();
722 while (!c.isSpace() && cc != '#' && cc != ')') {
723 includeFile += c;
724 SKIP_CHAR();
725 }
726 SKIP_SPACES();
727 if (cc != ')')
728 location.fatal(tr("Bad include syntax"));
729 SKIP_CHAR();
730 SKIP_SPACES();
731 if (cc != '#' && cc != '\n')
732 location.fatal(tr("Trailing garbage"));
733
734 /*
735 Here is the recursive call.
736 */
737 load(location,
738 QFileInfo(QFileInfo(fileName).dir(), includeFile)
739 .filePath());
740 }
741 else {
742 /*
743 It wasn't an include statement, so it;s something else.
744 */
745 if (cc == '+') {
746 plus = true;
747 SKIP_CHAR();
748 }
749 if (cc != '=')
750 location.fatal(tr("Expected '=' or '+=' after key"));
751 SKIP_CHAR();
752 SKIP_SPACES();
753
754 for (;;) {
755 if (cc == '\\') {
756 int metaCharPos;
757
758 SKIP_CHAR();
759 if (cc == '\n') {
760 SKIP_CHAR();
761 }
762 else if (cc > '0' && cc < '8') {
763 word += QChar(c.digitValue());
764 SKIP_CHAR();
765 }
766 else if ((metaCharPos = QString::fromLatin1("abfnrtv").indexOf(c)) != -1) {
767 word += "\a\b\f\n\r\t\v"[metaCharPos];
768 SKIP_CHAR();
769 }
770 else {
771 PUT_CHAR();
772 }
773 }
774 else if (c.isSpace() || cc == '#') {
775 if (inQuote) {
776 if (cc == '\n')
777 location.fatal(tr("Unterminated string"));
778 PUT_CHAR();
779 }
780 else {
781 if (!word.isEmpty()) {
782 if (metWord)
783 stringValue += QLatin1Char(' ');
784 stringValue += word;
785 stringListValue << word;
786 metWord = true;
787 word.clear();
788 prevWordQuoted = false;
789 }
790 if (cc == '\n' || cc == '#')
791 break;
792 SKIP_SPACES();
793 }
794 }
795 else if (cc == '"') {
796 if (inQuote) {
797 if (!prevWordQuoted)
798 stringValue += QLatin1Char(' ');
799 stringValue += word;
800 if (!word.isEmpty())
801 stringListValue << word;
802 metWord = true;
803 word.clear();
804 prevWordQuoted = true;
805 }
806 inQuote = !inQuote;
807 SKIP_CHAR();
808 }
809 else if (cc == '$') {
810 QString var;
811 SKIP_CHAR();
812 while (c.isLetterOrNumber() || cc == '_') {
813 var += c;
814 SKIP_CHAR();
815 }
816 if (!var.isEmpty()) {
817 char *val = getenv(var.toLatin1().data());
818 if (val == 0) {
819 location.fatal(tr("Environment variable '%1' undefined").arg(var));
820 }
821 else {
822 word += QString(val);
823 }
824 }
825 }
826 else {
827 if (!inQuote && cc == '=')
828 location.fatal(tr("Unexpected '='"));
829 PUT_CHAR();
830 }
831 }
832
833 QStringList::ConstIterator key = keys.begin();
834 while (key != keys.end()) {
835 if (!keySyntax.exactMatch(*key))
836 keyLoc.fatal(tr("Invalid key '%1'").arg(*key));
837
838 if (plus) {
839 if (locMap[*key].isEmpty()) {
840 locMap[*key] = keyLoc;
841 }
842 else {
843 locMap[*key].setEtc(true);
844 }
845 if (stringValueMap[*key].isEmpty()) {
846 stringValueMap[*key] = stringValue;
847 }
848 else {
849 stringValueMap[*key] +=
850 QLatin1Char(' ') + stringValue;
851 }
852 stringListValueMap[*key] += stringListValue;
853 }
854 else {
855 locMap[*key] = keyLoc;
856 stringValueMap[*key] = stringValue;
857 stringListValueMap[*key] = stringListValue;
858 }
859 ++key;
860 }
861 }
862 }
863 else {
864 location.fatal(tr("Unexpected character '%1' at beginning of line")
865 .arg(c));
866 }
867 }
868}
869
870QStringList Config::getFilesHere(const QString& dir,
871 const QString& nameFilter,
872 const QSet<QString> &excludedDirs)
873{
874 QStringList result;
875 if (excludedDirs.contains(dir))
876 return result;
877
878 QDir dirInfo(dir);
879 QStringList fileNames;
880 QStringList::const_iterator fn;
881
882 dirInfo.setNameFilters(nameFilter.split(' '));
883 dirInfo.setSorting(QDir::Name);
884 dirInfo.setFilter(QDir::Files);
885 fileNames = dirInfo.entryList();
886 fn = fileNames.constBegin();
887 while (fn != fileNames.constEnd()) {
888 if (!fn->startsWith(QLatin1Char('~')))
889 result.append(dirInfo.filePath(*fn));
890 ++fn;
891 }
892
893 dirInfo.setNameFilters(QStringList("*"));
894 dirInfo.setFilter(QDir::Dirs|QDir::NoDotAndDotDot);
895 fileNames = dirInfo.entryList();
896 fn = fileNames.constBegin();
897 while (fn != fileNames.constEnd()) {
898 result += getFilesHere(dirInfo.filePath(*fn), nameFilter, excludedDirs);
899 ++fn;
900 }
901 return result;
902}
903
904QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.