pathfinder.cpp
1 //------------------------------------------------------------------------------
2 // pathfinder.cpp
3 //------------------------------------------------------------------------------
4 //
5 // This library is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU Lesser General Public
7 // License as published by the Free Software Foundation; either
8 // version 2.1 of the License, or (at your option) any later version.
9 //
10 // This library is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 // Lesser General Public License for more details.
14 //
15 // You should have received a copy of the GNU Lesser General Public
16 // License along with this library; if not, write to the Free Software
17 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18 // 02110-1301 USA
19 //
20 //------------------------------------------------------------------------------
21 // Copyright (C) 2009 "Zalewa" <zalewapl@gmail.com>
22 //------------------------------------------------------------------------------
23 #include "pathfinder.h"
24 
25 #include "configuration/doomseekerconfig.h"
26 #include "datapaths.h"
27 #include "fileseeker.h"
28 #include "log.h"
29 #include "pathfinder/filesearchpath.h"
30 #include "strings.hpp"
31 #include "templatedpathresolver.h"
32 
33 #include <cstdlib>
34 #include <QCoreApplication>
35 #include <QDir>
36 #include <QFileInfo>
37 #include <QScopedPointer>
38 #include <QSharedPointer>
39 
40 DClass<PathFinderResult>
41 {
42 public:
43  QStringList foundFiles;
44  QStringList missingFiles;
45 };
46 
47 
48 DPointered(PathFinderResult)
49 
50 
52 {
53 }
54 
55 PathFinderResult::~PathFinderResult()
56 {
57 }
58 
60 {
61  return d->foundFiles;
62 }
63 
64 const QStringList &PathFinderResult::foundFiles() const
65 {
66  return d->foundFiles;
67 }
68 
70 {
71  return d->missingFiles;
72 }
73 
74 const QStringList &PathFinderResult::missingFiles() const
75 {
76  return d->missingFiles;
77 }
78 
80 
81 DClass<PathFinder>
82 {
83 public:
84  QSharedPointer <QList<FileSearchPath> > searchPaths;
85 
86  QString resolveDir(const QString &dir)
87  {
88  QFileInfo fileInfo(gDoomseekerTemplatedPathResolver().resolve(dir));
89  if (fileInfo.isSymLink())
90  fileInfo = QFileInfo(fileInfo.symLinkTarget());
91 
92  if (fileInfo.isBundle())
93  return fileInfo.absoluteFilePath() + "/Contents/MacOS";
94  else
95  {
96  if (fileInfo.isFile())
97  return fileInfo.absoluteDir().absolutePath();
98  else
99  return fileInfo.absoluteFilePath();
100  }
101  }
102 };
103 
104 
105 DPointered(PathFinder)
106 
107 
109 {
110  d->searchPaths.reset(new QList<FileSearchPath>());
111  auto resolver = gDoomseekerTemplatedPathResolver();
112  d->searchPaths->append(
113  FileSearchPath::resolveTemplated(resolver,
114  gConfig.combinedWadseekPaths()));
115  removeUnneededPaths();
116 }
117 
118 PathFinder::PathFinder(const QStringList &paths)
119 {
120  d->searchPaths.reset(new QList<FileSearchPath>());
121  for (const QString &path : paths)
122  d->searchPaths->append(path);
123  removeUnneededPaths();
124 }
125 
126 PathFinder::~PathFinder()
127 {
128 }
129 
130 PathFinder PathFinder::genericPathFinder(const QStringList &suffixes)
131 {
132  QStringList paths;
133  #if defined(Q_OS_WIN32)
134  paths << "." << ".."
135  << QCoreApplication::applicationDirPath()
136  << QDir(QCoreApplication::applicationDirPath()).filePath("..")
137  << DataPaths::programFilesDirectory(DataPaths::x64)
138  << DataPaths::programFilesDirectory(DataPaths::x86);
139  #else
140  paths << "/usr/bin" << "/usr/local/bin" << "/usr/share/bin"
141  << "/usr/games/" << "/usr/local/games/"
142  << "/usr/share/games/" << QCoreApplication::applicationDirPath() << ".";
143  #endif
144  QStringList pathsCopy(paths);
145  for (const QString &path : pathsCopy)
146  {
147  for (const QString &suffix : suffixes)
148  {
149  paths << Strings::combinePaths(path, suffix);
150  }
151  }
152  return PathFinder(paths);
153 }
154 
155 void PathFinder::addPrioritySearchDir(const QString &dir)
156 {
157  d->searchPaths->prepend(d->resolveDir(dir));
158  removeUnneededPaths();
159 }
160 
161 void PathFinder::addSearchDir(const QString &dir)
162 {
163  d->searchPaths->append(d->resolveDir(dir));
164  removeUnneededPaths();
165 }
166 
167 QString PathFinder::findFile(const QString &fileName) const
168 {
169  if (d->searchPaths->count() == 0)
170  return QString();
171  QScopedPointer<FileSeeker> seeker(FileSeeker::createSeeker(d->searchPaths));
172  QString result = seeker->findFile(fileName);
173  return result;
174 }
175 
176 PathFinderResult PathFinder::findFiles(const QStringList &files) const
177 {
178  PathFinderResult result;
179  for (const QString file : files)
180  {
181  QString filePath = findFile(file);
182  if (filePath.isNull())
183  {
184  result.missingFiles() << file;
185  }
186  else
187  {
188  result.foundFiles() << filePath;
189  }
190  }
191 
192  return result;
193 }
194 
195 void PathFinder::removeUnneededPaths() const
196 {
197  Qt::CaseSensitivity caseSensitivity;
198  #ifdef Q_OS_WIN32
199  caseSensitivity = Qt::CaseInsensitive;
200  #else
201  caseSensitivity = Qt::CaseSensitive;
202  #endif
203 
204  for (int mainIterator = 0; mainIterator < d->searchPaths->length(); ++mainIterator)
205  {
206  for (int subIterator = mainIterator + 1; subIterator < d->searchPaths->length(); ++subIterator)
207  {
208  const FileSearchPath &mainPath = d->searchPaths->at(mainIterator);
209  const FileSearchPath &subPath = d->searchPaths->at(subIterator);
210 
211  // are they equal?
212  if (QDir(mainPath.path()).absolutePath().compare(QDir(subPath.path()).absolutePath(), caseSensitivity) == 0)
213  {
214  (*d->searchPaths.data())[mainIterator]
215  .setRecursive(mainPath.isRecursive() || subPath.isRecursive());
216  d->searchPaths->removeAt(subIterator);
217  --subIterator;
218  continue;
219  }
220 
221  // is sub inside main and main is recursive?
222  if (mainPath.isRecursive() &&
223  QDir(subPath.path()).absolutePath().startsWith(QDir(mainPath.path()).absolutePath(), caseSensitivity))
224  {
225  d->searchPaths->removeAt(subIterator);
226  subIterator = mainIterator;
227  continue;
228  }
229 
230  // is main inside sub and sub is recursive?
231  if (subPath.isRecursive() &&
232  QDir(mainPath.path()).absolutePath().startsWith(QDir(subPath.path()).absolutePath(), caseSensitivity))
233  {
234  d->searchPaths->removeAt(mainIterator);
235  mainIterator = 0;
236  break;
237  }
238  }
239  }
240 }