svcore  1.9
ResourceFinder.cpp
Go to the documentation of this file.
00001 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
00002 
00003 /*
00004     Sonic Visualiser
00005     An audio file viewer and annotation editor.
00006     Centre for Digital Music, Queen Mary, University of London.
00007     
00008     This program is free software; you can redistribute it and/or
00009     modify it under the terms of the GNU General Public License as
00010     published by the Free Software Foundation; either version 2 of the
00011     License, or (at your option) any later version.  See the file
00012     COPYING included with this distribution for more information.
00013 */
00014 
00015 /*
00016    This is a modified version of a source file from the 
00017    Rosegarden MIDI and audio sequencer and notation editor.
00018    This file copyright 2005-2011 Chris Cannam and the Rosegarden
00019    development team.
00020 */
00021 
00022 #include "ResourceFinder.h"
00023 
00024 #include <QDir>
00025 #include <QFileInfo>
00026 #include <QStringList>
00027 #include <QProcess>
00028 #include <QCoreApplication>
00029 
00030 #if QT_VERSION >= 0x050000
00031 #include <QStandardPaths>
00032 #endif
00033 
00034 #include <cstdlib>
00035 #include <iostream>
00036 
00061 QStringList
00062 ResourceFinder::getSystemResourcePrefixList()
00063 {
00064     // returned in order of priority
00065 
00066     QStringList list;
00067 
00068 #ifdef Q_OS_WIN32
00069     char *programFiles = getenv("ProgramFiles");
00070     if (programFiles && programFiles[0]) {
00071         list << QString("%1/%2/%3")
00072             .arg(programFiles)
00073             .arg(qApp->organizationName())
00074             .arg(qApp->applicationName());
00075     } else {
00076         list << QString("C:/Program Files/%1/%2")
00077             .arg(qApp->organizationName())
00078             .arg(qApp->applicationName());
00079     }
00080 #else
00081 #ifdef Q_OS_MAC
00082     list << QString("/Library/Application Support/%1")
00083         .arg(qApp->applicationName());
00084 #else
00085     list << QString("/usr/local/share/%1")
00086         .arg(qApp->applicationName());
00087     list << QString("/usr/share/%1")
00088         .arg(qApp->applicationName());
00089 #endif
00090 #endif    
00091 
00092     return list;
00093 }
00094 
00095 static QString
00096 getOldStyleUserResourcePrefix()
00097 {
00098 #ifdef Q_OS_WIN32
00099     // This is awkward and does not work correctly for non-ASCII home
00100     // directory names, hence getNewStyleUserResourcePrefix() below
00101     char *homedrive = getenv("HOMEDRIVE");
00102     char *homepath = getenv("HOMEPATH");
00103     QString home;
00104     if (homedrive && homepath) {
00105         home = QString("%1%2").arg(homedrive).arg(homepath);
00106     } else {
00107         home = QDir::home().absolutePath();
00108     }
00109     if (home == "") return "";
00110     return QString("%1/.%2").arg(home).arg(qApp->applicationName()); 
00111 #else
00112     char *home = getenv("HOME");
00113     if (!home || !home[0]) return "";
00114 #ifdef Q_OS_MAC
00115     return QString("%1/Library/Application Support/%2")
00116         .arg(home)
00117         .arg(qApp->applicationName());
00118 #else
00119     return QString("%1/.local/share/%2")
00120         .arg(home)
00121         .arg(qApp->applicationName());
00122 #endif
00123 #endif
00124 }
00125 
00126 static QString
00127 getNewStyleUserResourcePrefix()
00128 {
00129 #if QT_VERSION >= 0x050000
00130     // This is expected to be much more reliable than
00131     // getOldStyleUserResourcePrefix(), but it returns a different
00132     // directory because it includes the organisation name (which is
00133     // fair enough). Hence migrateOldStyleResources() which moves
00134     // across any resources found in the old-style path the first time
00135     // we look for the new-style one
00136     return QStandardPaths::writableLocation(QStandardPaths::DataLocation);
00137 #else
00138     return getOldStyleUserResourcePrefix();
00139 #endif
00140 }
00141 
00142 static void
00143 migrateOldStyleResources()
00144 {
00145     QString oldPath = getOldStyleUserResourcePrefix();
00146     QString newPath = getNewStyleUserResourcePrefix();
00147     
00148     if (oldPath != newPath &&
00149         QDir(oldPath).exists() &&
00150         !QDir(newPath).exists()) {
00151 
00152         QDir d(oldPath);
00153         
00154         if (!d.mkpath(newPath)) {
00155             cerr << "WARNING: Failed to create new-style resource path \""
00156                  << newPath << "\" to migrate old resources to" << endl;
00157             return;
00158         }
00159 
00160         QDir target(newPath);
00161 
00162         bool success = true;
00163 
00164         QStringList entries
00165             (d.entryList(QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot));
00166 
00167         foreach (QString entry, entries) {
00168             if (d.rename(entry, target.filePath(entry))) {
00169                 cerr << "NOTE: Successfully moved resource \""
00170                      << entry << "\" from old resource path to new" << endl;
00171             } else {
00172                 cerr << "WARNING: Failed to move old resource \""
00173                      << entry << "\" from old location \""
00174                      << oldPath << "\" to new location \""
00175                      << newPath << "\"" << endl;
00176                 success = false;
00177             }
00178         }
00179 
00180         if (success) {
00181             if (!d.rmdir(oldPath)) {
00182                 cerr << "WARNING: Failed to remove old resource path \""
00183                      << oldPath << "\" after migrating " << entries.size()
00184                      << " resource(s) to new path \"" << newPath
00185                      << "\" (directory not empty?)" << endl;
00186             } else {
00187                 cerr << "NOTE: Successfully moved " << entries.size()
00188                      << " resource(s) from old resource "
00189                      << "path \"" << oldPath << "\" to new path \""
00190                      << newPath << "\"" << endl;
00191             }
00192         }
00193     }
00194 }
00195 
00196 QString
00197 ResourceFinder::getUserResourcePrefix()
00198 {
00199     migrateOldStyleResources();
00200     return getNewStyleUserResourcePrefix();
00201 }
00202 
00203 QStringList
00204 ResourceFinder::getResourcePrefixList()
00205 {
00206     // returned in order of priority
00207 
00208     QStringList list;
00209 
00210     QString user = getUserResourcePrefix();
00211     if (user != "") list << user;
00212 
00213     list << getSystemResourcePrefixList();
00214 
00215     list << ":"; // bundled resource location
00216 
00217     return list;
00218 }
00219 
00220 QString
00221 ResourceFinder::getResourcePath(QString resourceCat, QString fileName)
00222 {
00223     // We don't simply call getResourceDir here, because that returns
00224     // only the "installed file" location.  We also want to search the
00225     // bundled resources and user-saved files.
00226 
00227     QStringList prefixes = getResourcePrefixList();
00228     
00229     if (resourceCat != "") resourceCat = "/" + resourceCat;
00230 
00231     for (QStringList::const_iterator i = prefixes.begin();
00232          i != prefixes.end(); ++i) {
00233         
00234         QString prefix = *i;
00235 
00236         SVDEBUG << "ResourceFinder::getResourcePath: Looking up file \"" << fileName << "\" for category \"" << resourceCat << "\" in prefix \"" << prefix << "\"" << endl;
00237 
00238         QString path =
00239             QString("%1%2/%3").arg(prefix).arg(resourceCat).arg(fileName);
00240         if (QFileInfo(path).exists() && QFileInfo(path).isReadable()) {
00241             cerr << "Found it!" << endl;
00242             return path;
00243         }
00244     }
00245 
00246     return "";
00247 }
00248 
00249 QString
00250 ResourceFinder::getResourceDir(QString resourceCat)
00251 {
00252     // Returns only the "installed file" location
00253 
00254     QStringList prefixes = getSystemResourcePrefixList();
00255     
00256     if (resourceCat != "") resourceCat = "/" + resourceCat;
00257 
00258     for (QStringList::const_iterator i = prefixes.begin();
00259          i != prefixes.end(); ++i) {
00260         
00261         QString prefix = *i;
00262         QString path = QString("%1%2").arg(prefix).arg(resourceCat);
00263         if (QFileInfo(path).exists() &&
00264             QFileInfo(path).isDir() &&
00265             QFileInfo(path).isReadable()) {
00266             return path;
00267         }
00268     }
00269 
00270     return "";
00271 }
00272 
00273 QString
00274 ResourceFinder::getResourceSavePath(QString resourceCat, QString fileName)
00275 {
00276     QString dir = getResourceSaveDir(resourceCat);
00277     if (dir == "") return "";
00278 
00279     return dir + "/" + fileName;
00280 }
00281 
00282 QString
00283 ResourceFinder::getResourceSaveDir(QString resourceCat)
00284 {
00285     // Returns the "user" location
00286 
00287     QString user = getUserResourcePrefix();
00288     if (user == "") return "";
00289 
00290     if (resourceCat != "") resourceCat = "/" + resourceCat;
00291 
00292     QDir userDir(user);
00293     if (!userDir.exists()) {
00294         if (!userDir.mkpath(user)) {
00295             cerr << "ResourceFinder::getResourceSaveDir: ERROR: Failed to create user resource path \"" << user << "\"" << endl;
00296             return "";
00297         }
00298     }
00299 
00300     if (resourceCat != "") {
00301         QString save = QString("%1%2").arg(user).arg(resourceCat);
00302         QDir saveDir(save);
00303         if (!saveDir.exists()) {
00304             if (!saveDir.mkpath(save)) {
00305                 cerr << "ResourceFinder::getResourceSaveDir: ERROR: Failed to create user resource path \"" << save << "\"" << endl;
00306                 return "";
00307             }
00308         }
00309         return save;
00310     } else {
00311         return user;
00312     }
00313 }
00314 
00315 QStringList
00316 ResourceFinder::getResourceFiles(QString resourceCat, QString fileExt)
00317 {
00318     QStringList results;
00319     QStringList prefixes = getResourcePrefixList();
00320 
00321     QStringList filters;
00322     filters << QString("*.%1").arg(fileExt);
00323 
00324     for (QStringList::const_iterator i = prefixes.begin();
00325          i != prefixes.end(); ++i) {
00326         
00327         QString prefix = *i;
00328         QString path;
00329 
00330         if (resourceCat != "") {
00331             path = QString("%1/%2").arg(prefix).arg(resourceCat);
00332         } else {
00333             path = prefix;
00334         }
00335         
00336         QDir dir(path);
00337         if (!dir.exists()) continue;
00338 
00339         dir.setNameFilters(filters);
00340         QStringList entries = dir.entryList
00341             (QDir::Files | QDir::Readable, QDir::Name);
00342         
00343         for (QStringList::const_iterator j = entries.begin();
00344              j != entries.end(); ++j) {
00345             results << QString("%1/%2").arg(path).arg(*j);
00346         }
00347     }
00348 
00349     return results;
00350 }
00351 
00352 bool
00353 ResourceFinder::unbundleResource(QString resourceCat, QString fileName)
00354 {
00355     QString path = getResourcePath(resourceCat, fileName);
00356     
00357     if (!path.startsWith(':')) return true;
00358 
00359     // This is the lowest-priority alternative path for this
00360     // resource, so we know that there must be no installed copy.
00361     // Install one to the user location.
00362     SVDEBUG << "ResourceFinder::unbundleResource: File " << fileName << " is bundled, un-bundling it" << endl;
00363     QString target = getResourceSavePath(resourceCat, fileName);
00364     QFile file(path);
00365     if (!file.copy(target)) {
00366         cerr << "ResourceFinder::unbundleResource: ERROR: Failed to un-bundle resource file \"" << fileName << "\" to user location \"" << target << "\"" << endl;
00367         return false;
00368     }
00369 
00370     QFile chmod(target);
00371     chmod.setPermissions(QFile::ReadOwner |
00372                          QFile::ReadUser  | /* for potential platform-independence */
00373                          QFile::ReadGroup |
00374                          QFile::ReadOther |
00375                          QFile::WriteOwner|
00376                          QFile::WriteUser); /* for potential platform-independence */
00377 
00378     return true;
00379 }
00380