ResourceFinder.cpp
Go to the documentation of this file.
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
2 
3 /*
4  Sonic Visualiser
5  An audio file viewer and annotation editor.
6  Centre for Digital Music, Queen Mary, University of London.
7 
8  This program is free software; you can redistribute it and/or
9  modify it under the terms of the GNU General Public License as
10  published by the Free Software Foundation; either version 2 of the
11  License, or (at your option) any later version. See the file
12  COPYING included with this distribution for more information.
13 */
14 
15 /*
16  This is a modified version of a source file from the
17  Rosegarden MIDI and audio sequencer and notation editor.
18  This file copyright 2005-2011 Chris Cannam and the Rosegarden
19  development team.
20 */
21 
22 #include "ResourceFinder.h"
23 
24 #include <QDir>
25 #include <QFileInfo>
26 #include <QStringList>
27 #include <QProcess>
28 #include <QCoreApplication>
29 
30 #if QT_VERSION >= 0x050000
31 #include <QStandardPaths>
32 #endif
33 
34 #include <cstdlib>
35 #include <iostream>
36 #include <stdexcept>
37 
38 #include "system/System.h"
39 
64 QStringList
66 {
67  // returned in order of priority
68 
69  QStringList list;
70 
71 #ifdef Q_OS_WIN32
72  std::string programFiles;
73  (void)getEnvUtf8("ProgramFiles", programFiles);
74  if (programFiles != "") {
75  list << QString("%1/%2/%3")
76  .arg(QString::fromStdString(programFiles))
77  .arg(qApp->organizationName())
78  .arg(qApp->applicationName());
79  } else {
80  list << QString("C:/Program Files/%1/%2")
81  .arg(qApp->organizationName())
82  .arg(qApp->applicationName());
83  }
84 #else
85 #ifdef Q_OS_MAC
86  list << QString("/Library/Application Support/%1")
87  .arg(qApp->applicationName());
88 #else
89  list << QString("/usr/local/share/%1")
90  .arg(qApp->applicationName());
91  list << QString("/usr/share/%1")
92  .arg(qApp->applicationName());
93 #endif
94 #endif
95 
96  return list;
97 }
98 
99 static QString
101 {
102 #ifdef Q_OS_WIN32
103  // This is awkward and does not work correctly for non-ASCII home
104  // directory names, hence getNewStyleUserResourcePrefix() below
105  char *homedrive = getenv("HOMEDRIVE");
106  char *homepath = getenv("HOMEPATH");
107  QString home;
108  if (homedrive && homepath) {
109  home = QString("%1%2").arg(homedrive).arg(homepath);
110  } else {
111  home = QDir::home().absolutePath();
112  }
113  if (home == "") return "";
114  return QString("%1/.%2").arg(home).arg(qApp->applicationName());
115 #else
116  char *home = getenv("HOME");
117  if (!home || !home[0]) return "";
118 #ifdef Q_OS_MAC
119  return QString("%1/Library/Application Support/%2")
120  .arg(home)
121  .arg(qApp->applicationName());
122 #else
123  return QString("%1/.local/share/%2")
124  .arg(home)
125  .arg(qApp->applicationName());
126 #endif
127 #endif
128 }
129 
130 static QString
132 {
133  if (qApp->applicationName() == "" || qApp->organizationName() == "") {
134  cerr << "ERROR: Can't use ResourceFinder before setting application and organization name" << endl;
135  throw std::logic_error("Can't use ResourceFinder before setting application and organization name");
136  }
137 
138 #if QT_VERSION >= 0x050000
139 
140  // This is expected to be much more reliable than
141  // getOldStyleUserResourcePrefix(), but it returns a different
142  // directory because it includes the organisation name (which is
143  // fair enough). Hence migrateOldStyleResources() which moves
144  // across any resources found in the old-style path the first time
145  // we look for the new-style one
146 #if QT_VERSION >= 0x050400
147  return QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
148 #else
149  cerr << "WARNING: ResourceFinder::getOldStyleUserResourcePrefix: Building with older version of Qt (pre 5.4), resource location may be incompatible with future versions" << endl;
150  return QStandardPaths::writableLocation(QStandardPaths::DataLocation);
151 #endif
152 
153 #else
154  cerr << "WARNING: ResourceFinder::getOldStyleUserResourcePrefix: Building with very old version of Qt (pre 5.0?), resource location may be incompatible with future versions" << endl;
156 #endif
157 }
158 
159 static void
161 {
162  QString oldPath = getOldStyleUserResourcePrefix();
163  QString newPath = getNewStyleUserResourcePrefix();
164 
165  if (oldPath != newPath &&
166  QDir(oldPath).exists() &&
167  !QDir(newPath).exists()) {
168 
169  QDir d(oldPath);
170 
171  if (!d.mkpath(newPath)) {
172  cerr << "WARNING: Failed to create new-style resource path \""
173  << newPath << "\" to migrate old resources to" << endl;
174  return;
175  }
176 
177  QDir target(newPath);
178 
179  bool success = true;
180 
181  QStringList entries
182  (d.entryList(QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot));
183 
184  foreach (QString entry, entries) {
185  if (d.rename(entry, target.filePath(entry))) {
186  cerr << "NOTE: Successfully moved resource \""
187  << entry << "\" from old resource path to new" << endl;
188  } else {
189  cerr << "WARNING: Failed to move old resource \""
190  << entry << "\" from old location \""
191  << oldPath << "\" to new location \""
192  << newPath << "\"" << endl;
193  success = false;
194  }
195  }
196 
197  if (success) {
198  if (!d.rmdir(oldPath)) {
199  cerr << "WARNING: Failed to remove old resource path \""
200  << oldPath << "\" after migrating " << entries.size()
201  << " resource(s) to new path \"" << newPath
202  << "\" (directory not empty?)" << endl;
203  } else {
204  cerr << "NOTE: Successfully moved " << entries.size()
205  << " resource(s) from old resource "
206  << "path \"" << oldPath << "\" to new path \""
207  << newPath << "\"" << endl;
208  }
209  }
210  }
211 }
212 
213 QString
215 {
218 }
219 
220 QStringList
222 {
223  // returned in order of priority
224 
225  QStringList list;
226 
227  QString user = getUserResourcePrefix();
228  if (user != "") list << user;
229 
230  list << getSystemResourcePrefixList();
231 
232  list << ":"; // bundled resource location
233 
234  return list;
235 }
236 
237 QString
238 ResourceFinder::getResourcePath(QString resourceCat, QString fileName)
239 {
240  // We don't simply call getResourceDir here, because that returns
241  // only the "installed file" location. We also want to search the
242  // bundled resources and user-saved files.
243 
244  QStringList prefixes = getResourcePrefixList();
245 
246  if (resourceCat != "") resourceCat = "/" + resourceCat;
247 
248  for (QStringList::const_iterator i = prefixes.begin();
249  i != prefixes.end(); ++i) {
250 
251  QString prefix = *i;
252 
253 // cerr << "ResourceFinder::getResourcePath: Looking up file \"" << fileName << "\" for category \"" << resourceCat << "\" in prefix \"" << prefix << "\"" << endl;
254 
255  QString path =
256  QString("%1%2/%3").arg(prefix).arg(resourceCat).arg(fileName);
257  if (QFileInfo(path).exists() && QFileInfo(path).isReadable()) {
258 // cerr << "Found it!" << endl;
259  return path;
260  }
261  }
262 
263  return "";
264 }
265 
266 QString
267 ResourceFinder::getResourceDir(QString resourceCat)
268 {
269  // Returns only the "installed file" location
270 
271  QStringList prefixes = getSystemResourcePrefixList();
272 
273  if (resourceCat != "") resourceCat = "/" + resourceCat;
274 
275  for (QStringList::const_iterator i = prefixes.begin();
276  i != prefixes.end(); ++i) {
277 
278  QString prefix = *i;
279  QString path = QString("%1%2").arg(prefix).arg(resourceCat);
280  if (QFileInfo(path).exists() &&
281  QFileInfo(path).isDir() &&
282  QFileInfo(path).isReadable()) {
283  return path;
284  }
285  }
286 
287  return "";
288 }
289 
290 QString
291 ResourceFinder::getResourceSavePath(QString resourceCat, QString fileName)
292 {
293  QString dir = getResourceSaveDir(resourceCat);
294  if (dir == "") return "";
295 
296  return dir + "/" + fileName;
297 }
298 
299 QString
301 {
302  // Returns the "user" location
303 
304  QString user = getUserResourcePrefix();
305  if (user == "") return "";
306 
307  if (resourceCat != "") resourceCat = "/" + resourceCat;
308 
309  QDir userDir(user);
310  if (!userDir.exists()) {
311  if (!userDir.mkpath(user)) {
312  cerr << "ResourceFinder::getResourceSaveDir: ERROR: Failed to create user resource path \"" << user << "\"" << endl;
313  return "";
314  }
315  }
316 
317  if (resourceCat != "") {
318  QString save = QString("%1%2").arg(user).arg(resourceCat);
319  QDir saveDir(save);
320  if (!saveDir.exists()) {
321  if (!saveDir.mkpath(save)) {
322  cerr << "ResourceFinder::getResourceSaveDir: ERROR: Failed to create user resource path \"" << save << "\"" << endl;
323  return "";
324  }
325  }
326  return save;
327  } else {
328  return user;
329  }
330 }
331 
332 QStringList
333 ResourceFinder::getResourceFiles(QString resourceCat, QString fileExt)
334 {
335  QStringList results;
336  QStringList prefixes = getResourcePrefixList();
337 
338  QStringList filters;
339  filters << QString("*.%1").arg(fileExt);
340 
341  for (QStringList::const_iterator i = prefixes.begin();
342  i != prefixes.end(); ++i) {
343 
344  QString prefix = *i;
345  QString path;
346 
347  if (resourceCat != "") {
348  path = QString("%1/%2").arg(prefix).arg(resourceCat);
349  } else {
350  path = prefix;
351  }
352 
353  QDir dir(path);
354  if (!dir.exists()) continue;
355 
356  dir.setNameFilters(filters);
357  QStringList entries = dir.entryList
358  (QDir::Files | QDir::Readable, QDir::Name);
359 
360  for (QStringList::const_iterator j = entries.begin();
361  j != entries.end(); ++j) {
362  results << QString("%1/%2").arg(path).arg(*j);
363  }
364  }
365 
366  return results;
367 }
368 
369 bool
370 ResourceFinder::unbundleResource(QString resourceCat, QString fileName)
371 {
372  QString path = getResourcePath(resourceCat, fileName);
373 
374  if (!path.startsWith(':')) return true;
375 
376  // This is the lowest-priority alternative path for this
377  // resource, so we know that there must be no installed copy.
378  // Install one to the user location.
379  SVDEBUG << "ResourceFinder::unbundleResource: File " << fileName << " is bundled, un-bundling it" << endl;
380  QString target = getResourceSavePath(resourceCat, fileName);
381  QFile file(path);
382  if (!file.copy(target)) {
383  cerr << "ResourceFinder::unbundleResource: ERROR: Failed to un-bundle resource file \"" << fileName << "\" to user location \"" << target << "\"" << endl;
384  return false;
385  }
386 
387  QFile chmod(target);
388  chmod.setPermissions(QFile::ReadOwner |
389  QFile::ReadUser | /* for potential platform-independence */
390  QFile::ReadGroup |
391  QFile::ReadOther |
392  QFile::WriteOwner|
393  QFile::WriteUser); /* for potential platform-independence */
394 
395  return true;
396 }
397 
QString getResourceSaveDir(QString resourceCat)
Return the true file path for the location in which resource files in the given resource category sho...
bool getEnvUtf8(std::string variable, std::string &value)
Return the value of the given environment variable by reference.
Definition: System.cpp:344
QStringList getResourcePrefixList()
Return all root paths for resource installations for this application, in the order in which they wil...
QString getResourcePath(QString resourceCat, QString fileName)
Return the location (as a true file path, or a Qt4 ":"-prefixed resource path) of the file best match...
QStringList getResourceFiles(QString resourceCat, QString fileExt)
Return a list of full file paths for files with the given file extension, found in the given resource...
static QString getOldStyleUserResourcePrefix()
QStringList getSystemResourcePrefixList()
Return the root paths for systemwide resource installations for this application. ...
static QString getNewStyleUserResourcePrefix()
#define SVDEBUG
Definition: Debug.h:106
QString getUserResourcePrefix()
Return the root path for user-specific resource installation for this application (i...
bool unbundleResource(QString resourceCat, QString fileName)
If the named resource file in the given resource category is available only as a bundled resource...
QString getResourceSavePath(QString resourceCat, QString fileName)
Return the true file path for the location in which the named resource file in the given resource cat...
QString getResourceDir(QString resourceCat)
Return the true file path for installed resource files in the given resource category.
static void migrateOldStyleResources()