svcore  1.9
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 
61 QStringList
63 {
64  // returned in order of priority
65 
66  QStringList list;
67 
68 #ifdef Q_OS_WIN32
69  char *programFiles = getenv("ProgramFiles");
70  if (programFiles && programFiles[0]) {
71  list << QString("%1/%2/%3")
72  .arg(programFiles)
73  .arg(qApp->organizationName())
74  .arg(qApp->applicationName());
75  } else {
76  list << QString("C:/Program Files/%1/%2")
77  .arg(qApp->organizationName())
78  .arg(qApp->applicationName());
79  }
80 #else
81 #ifdef Q_OS_MAC
82  list << QString("/Library/Application Support/%1")
83  .arg(qApp->applicationName());
84 #else
85  list << QString("/usr/local/share/%1")
86  .arg(qApp->applicationName());
87  list << QString("/usr/share/%1")
88  .arg(qApp->applicationName());
89 #endif
90 #endif
91 
92  return list;
93 }
94 
95 static QString
97 {
98 #ifdef Q_OS_WIN32
99  // This is awkward and does not work correctly for non-ASCII home
100  // directory names, hence getNewStyleUserResourcePrefix() below
101  char *homedrive = getenv("HOMEDRIVE");
102  char *homepath = getenv("HOMEPATH");
103  QString home;
104  if (homedrive && homepath) {
105  home = QString("%1%2").arg(homedrive).arg(homepath);
106  } else {
107  home = QDir::home().absolutePath();
108  }
109  if (home == "") return "";
110  return QString("%1/.%2").arg(home).arg(qApp->applicationName());
111 #else
112  char *home = getenv("HOME");
113  if (!home || !home[0]) return "";
114 #ifdef Q_OS_MAC
115  return QString("%1/Library/Application Support/%2")
116  .arg(home)
117  .arg(qApp->applicationName());
118 #else
119  return QString("%1/.local/share/%2")
120  .arg(home)
121  .arg(qApp->applicationName());
122 #endif
123 #endif
124 }
125 
126 static QString
128 {
129 #if QT_VERSION >= 0x050000
130  // This is expected to be much more reliable than
131  // getOldStyleUserResourcePrefix(), but it returns a different
132  // directory because it includes the organisation name (which is
133  // fair enough). Hence migrateOldStyleResources() which moves
134  // across any resources found in the old-style path the first time
135  // we look for the new-style one
136  return QStandardPaths::writableLocation(QStandardPaths::DataLocation);
137 #else
139 #endif
140 }
141 
142 static void
144 {
145  QString oldPath = getOldStyleUserResourcePrefix();
146  QString newPath = getNewStyleUserResourcePrefix();
147 
148  if (oldPath != newPath &&
149  QDir(oldPath).exists() &&
150  !QDir(newPath).exists()) {
151 
152  QDir d(oldPath);
153 
154  if (!d.mkpath(newPath)) {
155  cerr << "WARNING: Failed to create new-style resource path \""
156  << newPath << "\" to migrate old resources to" << endl;
157  return;
158  }
159 
160  QDir target(newPath);
161 
162  bool success = true;
163 
164  QStringList entries
165  (d.entryList(QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot));
166 
167  foreach (QString entry, entries) {
168  if (d.rename(entry, target.filePath(entry))) {
169  cerr << "NOTE: Successfully moved resource \""
170  << entry << "\" from old resource path to new" << endl;
171  } else {
172  cerr << "WARNING: Failed to move old resource \""
173  << entry << "\" from old location \""
174  << oldPath << "\" to new location \""
175  << newPath << "\"" << endl;
176  success = false;
177  }
178  }
179 
180  if (success) {
181  if (!d.rmdir(oldPath)) {
182  cerr << "WARNING: Failed to remove old resource path \""
183  << oldPath << "\" after migrating " << entries.size()
184  << " resource(s) to new path \"" << newPath
185  << "\" (directory not empty?)" << endl;
186  } else {
187  cerr << "NOTE: Successfully moved " << entries.size()
188  << " resource(s) from old resource "
189  << "path \"" << oldPath << "\" to new path \""
190  << newPath << "\"" << endl;
191  }
192  }
193  }
194 }
195 
196 QString
198 {
201 }
202 
203 QStringList
205 {
206  // returned in order of priority
207 
208  QStringList list;
209 
210  QString user = getUserResourcePrefix();
211  if (user != "") list << user;
212 
213  list << getSystemResourcePrefixList();
214 
215  list << ":"; // bundled resource location
216 
217  return list;
218 }
219 
220 QString
221 ResourceFinder::getResourcePath(QString resourceCat, QString fileName)
222 {
223  // We don't simply call getResourceDir here, because that returns
224  // only the "installed file" location. We also want to search the
225  // bundled resources and user-saved files.
226 
227  QStringList prefixes = getResourcePrefixList();
228 
229  if (resourceCat != "") resourceCat = "/" + resourceCat;
230 
231  for (QStringList::const_iterator i = prefixes.begin();
232  i != prefixes.end(); ++i) {
233 
234  QString prefix = *i;
235 
236  SVDEBUG << "ResourceFinder::getResourcePath: Looking up file \"" << fileName << "\" for category \"" << resourceCat << "\" in prefix \"" << prefix << "\"" << endl;
237 
238  QString path =
239  QString("%1%2/%3").arg(prefix).arg(resourceCat).arg(fileName);
240  if (QFileInfo(path).exists() && QFileInfo(path).isReadable()) {
241  cerr << "Found it!" << endl;
242  return path;
243  }
244  }
245 
246  return "";
247 }
248 
249 QString
250 ResourceFinder::getResourceDir(QString resourceCat)
251 {
252  // Returns only the "installed file" location
253 
254  QStringList prefixes = getSystemResourcePrefixList();
255 
256  if (resourceCat != "") resourceCat = "/" + resourceCat;
257 
258  for (QStringList::const_iterator i = prefixes.begin();
259  i != prefixes.end(); ++i) {
260 
261  QString prefix = *i;
262  QString path = QString("%1%2").arg(prefix).arg(resourceCat);
263  if (QFileInfo(path).exists() &&
264  QFileInfo(path).isDir() &&
265  QFileInfo(path).isReadable()) {
266  return path;
267  }
268  }
269 
270  return "";
271 }
272 
273 QString
274 ResourceFinder::getResourceSavePath(QString resourceCat, QString fileName)
275 {
276  QString dir = getResourceSaveDir(resourceCat);
277  if (dir == "") return "";
278 
279  return dir + "/" + fileName;
280 }
281 
282 QString
284 {
285  // Returns the "user" location
286 
287  QString user = getUserResourcePrefix();
288  if (user == "") return "";
289 
290  if (resourceCat != "") resourceCat = "/" + resourceCat;
291 
292  QDir userDir(user);
293  if (!userDir.exists()) {
294  if (!userDir.mkpath(user)) {
295  cerr << "ResourceFinder::getResourceSaveDir: ERROR: Failed to create user resource path \"" << user << "\"" << endl;
296  return "";
297  }
298  }
299 
300  if (resourceCat != "") {
301  QString save = QString("%1%2").arg(user).arg(resourceCat);
302  QDir saveDir(save);
303  if (!saveDir.exists()) {
304  if (!saveDir.mkpath(save)) {
305  cerr << "ResourceFinder::getResourceSaveDir: ERROR: Failed to create user resource path \"" << save << "\"" << endl;
306  return "";
307  }
308  }
309  return save;
310  } else {
311  return user;
312  }
313 }
314 
315 QStringList
316 ResourceFinder::getResourceFiles(QString resourceCat, QString fileExt)
317 {
318  QStringList results;
319  QStringList prefixes = getResourcePrefixList();
320 
321  QStringList filters;
322  filters << QString("*.%1").arg(fileExt);
323 
324  for (QStringList::const_iterator i = prefixes.begin();
325  i != prefixes.end(); ++i) {
326 
327  QString prefix = *i;
328  QString path;
329 
330  if (resourceCat != "") {
331  path = QString("%1/%2").arg(prefix).arg(resourceCat);
332  } else {
333  path = prefix;
334  }
335 
336  QDir dir(path);
337  if (!dir.exists()) continue;
338 
339  dir.setNameFilters(filters);
340  QStringList entries = dir.entryList
341  (QDir::Files | QDir::Readable, QDir::Name);
342 
343  for (QStringList::const_iterator j = entries.begin();
344  j != entries.end(); ++j) {
345  results << QString("%1/%2").arg(path).arg(*j);
346  }
347  }
348 
349  return results;
350 }
351 
352 bool
353 ResourceFinder::unbundleResource(QString resourceCat, QString fileName)
354 {
355  QString path = getResourcePath(resourceCat, fileName);
356 
357  if (!path.startsWith(':')) return true;
358 
359  // This is the lowest-priority alternative path for this
360  // resource, so we know that there must be no installed copy.
361  // Install one to the user location.
362  SVDEBUG << "ResourceFinder::unbundleResource: File " << fileName << " is bundled, un-bundling it" << endl;
363  QString target = getResourceSavePath(resourceCat, fileName);
364  QFile file(path);
365  if (!file.copy(target)) {
366  cerr << "ResourceFinder::unbundleResource: ERROR: Failed to un-bundle resource file \"" << fileName << "\" to user location \"" << target << "\"" << endl;
367  return false;
368  }
369 
370  QFile chmod(target);
371  chmod.setPermissions(QFile::ReadOwner |
372  QFile::ReadUser | /* for potential platform-independence */
373  QFile::ReadGroup |
374  QFile::ReadOther |
375  QFile::WriteOwner|
376  QFile::WriteUser); /* for potential platform-independence */
377 
378  return true;
379 }
380 
QString getResourceSaveDir(QString resourceCat)
Return the true file path for the location in which resource files in the given resource category sho...
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:42
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...
static char * prefix
Definition: Debug.cpp:33
QString getResourceDir(QString resourceCat)
Return the true file path for installed resource files in the given resource category.
static void migrateOldStyleResources()