# HG changeset patch # User Chris Cannam # Date 1528724409 -3600 # Node ID 7459f4c4d7c3a11435daf615b01cbb7025ed8bf4 # Parent 85e9b7b31a8d5b7a7da3252a6ea9b7d328483651# Parent c014839f49c79253cc2f75418c5c261c9a574ee9 Merge from branch plugin-path-config diff -r 85e9b7b31a8d -r 7459f4c4d7c3 base/ResourceFinder.cpp --- a/base/ResourceFinder.cpp Thu May 24 16:30:55 2018 +0100 +++ b/base/ResourceFinder.cpp Mon Jun 11 14:40:09 2018 +0100 @@ -35,6 +35,8 @@ #include #include +#include "system/System.h" + /** Resource files may be found in three places: @@ -67,10 +69,11 @@ QStringList list; #ifdef Q_OS_WIN32 - char *programFiles = getenv("ProgramFiles"); - if (programFiles && programFiles[0]) { + std::string programFiles; + (void)getEnvUtf8("ProgramFiles", programFiles); + if (programFiles != "") { list << QString("%1/%2/%3") - .arg(programFiles) + .arg(QString::fromStdString(programFiles)) .arg(qApp->organizationName()) .arg(qApp->applicationName()); } else { diff -r 85e9b7b31a8d -r 7459f4c4d7c3 data/model/test/svcore-data-model-test.cpp --- a/data/model/test/svcore-data-model-test.cpp Thu May 24 16:30:55 2018 +0100 +++ b/data/model/test/svcore-data-model-test.cpp Mon Jun 11 14:40:09 2018 +0100 @@ -34,10 +34,10 @@ } if (bad > 0) { - SVCERR << "\n********* " << bad << " test suite(s) failed!\n" << endl; + SVCERR << "\n********* " << bad << " test suite(s) failed!\n" << endl; return 1; } else { - SVCERR << "All tests passed" << endl; + SVCERR << "All tests passed" << endl; return 0; } } diff -r 85e9b7b31a8d -r 7459f4c4d7c3 files.pri --- a/files.pri Thu May 24 16:30:55 2018 +0100 +++ b/files.pri Mon Jun 11 14:40:09 2018 +0100 @@ -108,6 +108,7 @@ plugin/NativeVampPluginFactory.h \ plugin/PiperVampPluginFactory.h \ plugin/PluginIdentifier.h \ + plugin/PluginPathSetter.h \ plugin/PluginXml.h \ plugin/RealTimePluginFactory.h \ plugin/RealTimePluginInstance.h \ @@ -220,6 +221,7 @@ plugin/NativeVampPluginFactory.cpp \ plugin/PiperVampPluginFactory.cpp \ plugin/PluginIdentifier.cpp \ + plugin/PluginPathSetter.cpp \ plugin/PluginXml.cpp \ plugin/RealTimePluginFactory.cpp \ plugin/RealTimePluginInstance.cpp \ diff -r 85e9b7b31a8d -r 7459f4c4d7c3 plugin/DSSIPluginFactory.cpp --- a/plugin/DSSIPluginFactory.cpp Thu May 24 16:30:55 2018 +0100 +++ b/plugin/DSSIPluginFactory.cpp Mon Jun 11 14:40:09 2018 +0100 @@ -39,6 +39,7 @@ #include "lrdf.h" #endif // HAVE_LRDF +using std::string; DSSIPluginFactory::DSSIPluginFactory() : LADSPAPluginFactory() @@ -206,38 +207,38 @@ DSSIPluginFactory::getPluginPath() { std::vector pathList; - std::string path; + string path; - char *cpath = getenv("DSSI_PATH"); - if (cpath) path = cpath; + (void)getEnvUtf8("DSSI_PATH", path); if (path == "") { path = DEFAULT_DSSI_PATH; - char *home = getenv("HOME"); - if (home) { - std::string::size_type f; - while ((f = path.find("$HOME")) != std::string::npos && + string home; + if (getEnvUtf8("HOME", home)) { + string::size_type f; + while ((f = path.find("$HOME")) != string::npos && f < path.length()) { path.replace(f, 5, home); } } #ifdef _WIN32 - const char *pfiles = getenv("ProgramFiles"); - if (!pfiles) pfiles = "C:\\Program Files"; - { - std::string::size_type f; - while ((f = path.find("%ProgramFiles%")) != std::string::npos && + string pfiles; + if (!getEnvUtf8("ProgramFiles", pfiles)) { + pfiles = "C:\\Program Files"; + } + + string::size_type f; + while ((f = path.find("%ProgramFiles%")) != string::npos && f < path.length()) { path.replace(f, 14, pfiles); } - } #endif } - std::string::size_type index = 0, newindex = 0; + string::size_type index = 0, newindex = 0; while ((newindex = path.find(PATH_SEPARATOR, index)) < path.size()) { pathList.push_back(path.substr(index, newindex - index).c_str()); @@ -347,7 +348,7 @@ } if (category == "") { - std::string name = rtd->name; + string name = rtd->name; if (name.length() > 4 && name.substr(name.length() - 4) == " VST") { if (descriptor->run_synth || descriptor->run_multiple_synths) { diff -r 85e9b7b31a8d -r 7459f4c4d7c3 plugin/DSSIPluginFactory.h --- a/plugin/DSSIPluginFactory.h Thu May 24 16:30:55 2018 +0100 +++ b/plugin/DSSIPluginFactory.h Mon Jun 11 14:40:09 2018 +0100 @@ -44,6 +44,8 @@ int blockSize, int channels); + static std::vector getPluginPath(); + protected: DSSIPluginFactory(); friend class RealTimePluginFactory; @@ -52,8 +54,6 @@ return PluginScan::DSSIPlugin; } - virtual std::vector getPluginPath(); - virtual std::vector getLRDFPath(QString &baseUri); virtual void discoverPluginsFrom(QString soName); diff -r 85e9b7b31a8d -r 7459f4c4d7c3 plugin/LADSPAPluginFactory.cpp --- a/plugin/LADSPAPluginFactory.cpp Thu May 24 16:30:55 2018 +0100 +++ b/plugin/LADSPAPluginFactory.cpp Mon Jun 11 14:40:09 2018 +0100 @@ -40,6 +40,7 @@ #include "lrdf.h" #endif // HAVE_LRDF +using std::string; LADSPAPluginFactory::LADSPAPluginFactory() { @@ -566,38 +567,38 @@ LADSPAPluginFactory::getPluginPath() { std::vector pathList; - std::string path; + string path; - char *cpath = getenv("LADSPA_PATH"); - if (cpath) path = cpath; + (void)getEnvUtf8("DSSI_PATH", path); if (path == "") { path = DEFAULT_LADSPA_PATH; - char *home = getenv("HOME"); - if (home) { - std::string::size_type f; - while ((f = path.find("$HOME")) != std::string::npos && + string home; + if (getEnvUtf8("HOME", home)) { + string::size_type f; + while ((f = path.find("$HOME")) != string::npos && f < path.length()) { path.replace(f, 5, home); } } #ifdef _WIN32 - const char *pfiles = getenv("ProgramFiles"); - if (!pfiles) pfiles = "C:\\Program Files"; - { - std::string::size_type f; - while ((f = path.find("%ProgramFiles%")) != std::string::npos && + string pfiles; + if (!getEnvUtf8("ProgramFiles", pfiles)) { + pfiles = "C:\\Program Files"; + } + + string::size_type f; + while ((f = path.find("%ProgramFiles%")) != string::npos && f < path.length()) { path.replace(f, 14, pfiles); } - } #endif } - std::string::size_type index = 0, newindex = 0; + string::size_type index = 0, newindex = 0; while ((newindex = path.find(PATH_SEPARATOR, index)) < path.size()) { pathList.push_back(path.substr(index, newindex - index).c_str()); @@ -734,7 +735,7 @@ QString category = m_taxonomy[identifier]; if (category == "") { - std::string name = rtd->name; + string name = rtd->name; if (name.length() > 4 && name.substr(name.length() - 4) == " VST") { category = "VST effects"; diff -r 85e9b7b31a8d -r 7459f4c4d7c3 plugin/LADSPAPluginFactory.h --- a/plugin/LADSPAPluginFactory.h Thu May 24 16:30:55 2018 +0100 +++ b/plugin/LADSPAPluginFactory.h Mon Jun 11 14:40:09 2018 +0100 @@ -63,6 +63,8 @@ float getPortQuantization(const LADSPA_Descriptor *, int port); int getPortDisplayHint(const LADSPA_Descriptor *, int port); + static std::vector getPluginPath(); + protected: LADSPAPluginFactory(); friend class RealTimePluginFactory; @@ -71,8 +73,6 @@ return PluginScan::LADSPAPlugin; } - virtual std::vector getPluginPath(); - virtual std::vector getLRDFPath(QString &baseUri); virtual void discoverPluginsFrom(QString soName); diff -r 85e9b7b31a8d -r 7459f4c4d7c3 plugin/PiperVampPluginFactory.cpp --- a/plugin/PiperVampPluginFactory.cpp Thu May 24 16:30:55 2018 +0100 +++ b/plugin/PiperVampPluginFactory.cpp Mon Jun 11 14:40:09 2018 +0100 @@ -217,7 +217,10 @@ string soname = QFileInfo(c.libraryPath).baseName().toStdString(); SVDEBUG << "INFO: For tag \"" << tag << "\" giving library " << soname << endl; from.push_back(soname); - m_libraries[QString::fromStdString(soname)] = c.libraryPath; + QString qsoname = QString::fromStdString(soname); + if (m_libraries.find(qsoname) == m_libraries.end()) { + m_libraries[qsoname] = c.libraryPath; + } } } diff -r 85e9b7b31a8d -r 7459f4c4d7c3 plugin/PluginPathSetter.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugin/PluginPathSetter.cpp Mon Jun 11 14:40:09 2018 +0100 @@ -0,0 +1,278 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Sonic Visualiser + An audio file viewer and annotation editor. + Centre for Digital Music, Queen Mary, University of London. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#include "PluginPathSetter.h" + +#include + +#include "RealTimePluginFactory.h" +#include "LADSPAPluginFactory.h" +#include "DSSIPluginFactory.h" + +#include +#include + +#include "system/System.h" +#include "base/Preferences.h" +#include "base/HelperExecPath.h" + +QMutex +PluginPathSetter::m_mutex; + +PluginPathSetter::Paths +PluginPathSetter::m_defaultPaths; + +PluginPathSetter::Paths +PluginPathSetter::m_environmentPaths; + +std::map +PluginPathSetter::m_originalEnvValues; + +PluginPathSetter::TypeKeys +PluginPathSetter::m_supportedKeys; + +using namespace std; + +PluginPathSetter::TypeKeys +PluginPathSetter::getSupportedKeys() +{ + QMutexLocker locker(&m_mutex); + + if (!m_supportedKeys.empty()) { + return m_supportedKeys; + } + + TypeKeys keys; + keys.push_back({ KnownPlugins::VampPlugin, KnownPlugins::FormatNative }); + + bool inProcess = Preferences::getInstance()->getRunPluginsInProcess(); + HelperExecPath hep(inProcess ? + HelperExecPath::NativeArchitectureOnly : + HelperExecPath::AllInstalled); + auto execs = hep.getHelperExecutables("vamp-plugin-load-checker"); + if (execs.size() > 1) { + keys.push_back({ + KnownPlugins::VampPlugin, KnownPlugins::FormatNonNative32Bit }); + } + + keys.push_back({ KnownPlugins::LADSPAPlugin, KnownPlugins::FormatNative }); + keys.push_back({ KnownPlugins::DSSIPlugin, KnownPlugins::FormatNative }); + + m_supportedKeys = keys; + return keys; +} + +// call with mutex held please +PluginPathSetter::Paths +PluginPathSetter::getEnvironmentPathsUncached(const TypeKeys &keys) +{ + Paths paths; + + for (auto k: keys) { + + KnownPlugins kp(k.second); + + auto path = kp.getPathFor(k.first); + QStringList qPath; + for (auto s: path) { + qPath.push_back(QString::fromStdString(s)); + } + + auto var = kp.getPathEnvironmentVariableFor(k.first); + QString qVar = QString::fromStdString(var); + + paths[k] = { qPath, qVar, true }; + } + + return paths; +} + +PluginPathSetter::Paths +PluginPathSetter::getDefaultPaths() +{ + TypeKeys keys = getSupportedKeys(); + + QMutexLocker locker(&m_mutex); + + Paths paths; + + for (auto k: keys) { + + KnownPlugins kp(k.second); + + auto path = kp.getDefaultPathFor(k.first); + QStringList qPath; + for (auto s: path) { + qPath.push_back(QString::fromStdString(s)); + } + + auto var = kp.getPathEnvironmentVariableFor(k.first); + QString qVar = QString::fromStdString(var); + + paths[k] = { qPath, qVar, true }; + } + + return paths; +} + +PluginPathSetter::Paths +PluginPathSetter::getEnvironmentPaths() +{ + TypeKeys keys = getSupportedKeys(); + + QMutexLocker locker(&m_mutex); + + if (!m_environmentPaths.empty()) { + return m_environmentPaths; + } + + m_environmentPaths = getEnvironmentPathsUncached(keys); + return m_environmentPaths; +} + +QString +PluginPathSetter::getSettingTagFor(TypeKey tk) +{ + string tag = KnownPlugins(tk.second).getTagFor(tk.first); + if (tk.second == KnownPlugins::FormatNonNative32Bit) { + tag += "-32"; + } + return QString::fromStdString(tag); +} + +PluginPathSetter::Paths +PluginPathSetter::getPaths() +{ + Paths paths = getEnvironmentPaths(); + + QSettings settings; + settings.beginGroup("Plugins"); + + for (auto p: paths) { + + TypeKey tk = p.first; + + QString settingTag = getSettingTagFor(tk); + + QStringList directories = + settings.value(QString("directories-%1").arg(settingTag), + p.second.directories) + .toStringList(); + QString envVariable = + settings.value(QString("env-variable-%1").arg(settingTag), + p.second.envVariable) + .toString(); + bool useEnvVariable = + settings.value(QString("use-env-variable-%1").arg(settingTag), + p.second.useEnvVariable) + .toBool(); + + string envVarStr = envVariable.toStdString(); + string currentValue; + (void)getEnvUtf8(envVarStr, currentValue); + + if (currentValue != "" && useEnvVariable) { + directories = QString::fromStdString(currentValue).split( +#ifdef Q_OS_WIN + ";" +#else + ":" +#endif + ); + } + + paths[tk] = { directories, envVariable, useEnvVariable }; + } + + settings.endGroup(); + + return paths; +} + +void +PluginPathSetter::savePathSettings(Paths paths) +{ + QSettings settings; + settings.beginGroup("Plugins"); + + for (auto p: paths) { + QString settingTag = getSettingTagFor(p.first); + settings.setValue(QString("directories-%1").arg(settingTag), + p.second.directories); + settings.setValue(QString("env-variable-%1").arg(settingTag), + p.second.envVariable); + settings.setValue(QString("use-env-variable-%1").arg(settingTag), + p.second.useEnvVariable); + } + + settings.endGroup(); +} + +QString +PluginPathSetter::getOriginalEnvironmentValue(QString envVariable) +{ + if (m_originalEnvValues.find(envVariable) != m_originalEnvValues.end()) { + return m_originalEnvValues.at(envVariable); + } else { + return QString(); + } +} + +void +PluginPathSetter::initialiseEnvironmentVariables() +{ + // Set the relevant environment variables from user configuration, + // so that later lookups through the standard APIs will follow the + // same paths as we have in the user config + + // First ensure the default paths have been recorded for later, so + // we don't erroneously re-read them from the environment + // variables we've just set + (void)getDefaultPaths(); + (void)getEnvironmentPaths(); + + Paths paths = getPaths(); + + for (auto p: paths) { + QString envVariable = p.second.envVariable; + string envVarStr = envVariable.toStdString(); + string currentValue; + getEnvUtf8(envVarStr, currentValue); + m_originalEnvValues[envVariable] = QString::fromStdString(currentValue); + if (currentValue != "" && p.second.useEnvVariable) { + // don't override + SVDEBUG << "PluginPathSetter: for environment variable " + << envVariable << ", useEnvVariable setting is false; " + << "leaving current value alone: it is \"" + << currentValue << "\"" << endl; + continue; + } + QString separator = +#ifdef Q_OS_WIN + ";" +#else + ":" +#endif + ; + QString proposedValue = p.second.directories.join(separator); + SVDEBUG << "PluginPathSetter: for environment variable " + << envVariable << ", useEnvVariable setting is true or " + << "variable is currently unset; " + << "changing value from \"" << currentValue + << "\" to setting preference of \"" << proposedValue + << "\"" << endl; + putEnvUtf8(envVarStr, proposedValue.toStdString()); + } +} + diff -r 85e9b7b31a8d -r 7459f4c4d7c3 plugin/PluginPathSetter.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugin/PluginPathSetter.h Mon Jun 11 14:40:09 2018 +0100 @@ -0,0 +1,85 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Sonic Visualiser + An audio file viewer and annotation editor. + Centre for Digital Music, Queen Mary, University of London. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#ifndef SV_PLUGIN_PATH_SETTER_H +#define SV_PLUGIN_PATH_SETTER_H + +#include +#include +#include + +#include + +#include "checker/knownplugins.h" + +class PluginPathSetter +{ +public: + typedef std::pair TypeKey; + + typedef std::vector TypeKeys; + + struct PathConfig { + QStringList directories; // Actual list of directories arising + // from user settings, environment + // variables, and defaults as + // appropriate + + QString envVariable; // Name of env var, e.g. LADSPA_PATH + + bool useEnvVariable; // True if env variable should override + // any user settings for this + }; + + typedef std::map Paths; + + /// Update *_PATH environment variables from the settings, on + /// application startup. Must be called exactly once, before any + /// of the other functions in this class has been called + static void initialiseEnvironmentVariables(); + + /// Return default values of paths only, without any environment + /// variables or user-defined preferences + static Paths getDefaultPaths(); + + /// Return paths arising from environment variables only, falling + /// back to the defaults, without any user-defined preferences + static Paths getEnvironmentPaths(); + + /// Return paths arising from user settings + environment + /// variables + defaults as appropriate + static Paths getPaths(); + + /// Save the given paths to the settings + static void savePathSettings(Paths paths); + + /// Return the original value observed on startup for the given + /// environment variable, if it is one of the variables used by a + /// known path config. + static QString getOriginalEnvironmentValue(QString envVariable); + +private: + static Paths m_defaultPaths; + static Paths m_environmentPaths; + static std::map m_originalEnvValues; + static TypeKeys m_supportedKeys; + static QMutex m_mutex; + + static std::vector getSupportedKeys(); + static Paths getEnvironmentPathsUncached(const TypeKeys &keys); + static QString getSettingTagFor(TypeKey); +}; + +#endif diff -r 85e9b7b31a8d -r 7459f4c4d7c3 plugin/PluginScan.cpp --- a/plugin/PluginScan.cpp Thu May 24 16:30:55 2018 +0100 +++ b/plugin/PluginScan.cpp Mon Jun 11 14:40:09 2018 +0100 @@ -19,9 +19,9 @@ #include "base/HelperExecPath.h" #ifdef HAVE_PLUGIN_CHECKER_HELPER -#include "checker/knownplugins.h" +#include "checker/knownplugincandidates.h" #else -class KnownPlugins {}; +class KnownPluginCandidates {}; #endif #include @@ -92,7 +92,7 @@ for (auto p: helpers) { try { - KnownPlugins *kp = new KnownPlugins + KnownPluginCandidates *kp = new KnownPluginCandidates (p.executable.toStdString(), m_logger); if (m_kp.find(p.tag) != m_kp.end()) { SVDEBUG << "WARNING: PluginScan::scan: Duplicate tag " << p.tag @@ -151,7 +151,7 @@ for (auto rec: m_kp) { - KnownPlugins *kp = rec.second; + KnownPluginCandidates *kp = rec.second; auto c = kp->getCandidateLibrariesFor(kpt); diff -r 85e9b7b31a8d -r 7459f4c4d7c3 plugin/PluginScan.h --- a/plugin/PluginScan.h Thu May 24 16:30:55 2018 +0100 +++ b/plugin/PluginScan.h Mon Jun 11 14:40:09 2018 +0100 @@ -20,7 +20,7 @@ #include #include -class KnownPlugins; +class KnownPluginCandidates; class PluginScan { @@ -75,7 +75,7 @@ mutable QMutex m_mutex; // while scanning; definitely can't multi-thread this - std::map m_kp; // tag -> KnownPlugins client + std::map m_kp; // tag -> KnownPlugins client bool m_succeeded; class Logger; diff -r 85e9b7b31a8d -r 7459f4c4d7c3 system/System.cpp --- a/system/System.cpp Thu May 24 16:30:55 2018 +0100 +++ b/system/System.cpp Mon Jun 11 14:40:09 2018 +0100 @@ -4,7 +4,7 @@ Sonic Visualiser An audio file viewer and annotation editor. Centre for Digital Music, Queen Mary, University of London. - This file copyright 2006 Chris Cannam and QMUL. + This file copyright 2006-2018 Chris Cannam and QMUL. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -63,7 +63,7 @@ } #endif - int gettimeofday(struct timeval *tv, void *tz) + int gettimeofday(struct timeval *tv, void * /* tz */) { union { long long ns100; @@ -328,4 +328,109 @@ double princarg(double a) { return mod(a + M_PI, -2 * M_PI) + M_PI; } float princargf(float a) { return float(princarg(a)); } +bool +getEnvUtf8(std::string variable, std::string &value) +{ + value = ""; + +#ifdef _WIN32 + int wvarlen = MultiByteToWideChar(CP_UTF8, 0, + variable.c_str(), int(variable.length()), + 0, 0); + if (wvarlen < 0) { + SVCERR << "WARNING: Unable to convert environment variable name " + << variable << " to wide characters" << endl; + return false; + } + + wchar_t *wvarbuf = new wchar_t[wvarlen + 1]; + (void)MultiByteToWideChar(CP_UTF8, 0, + variable.c_str(), int(variable.length()), + wvarbuf, wvarlen); + wvarbuf[wvarlen] = L'\0'; + + wchar_t *wvalue = _wgetenv(wvarbuf); + delete[] wvarbuf; + + if (!wvalue) { + return false; + } + + int wvallen = int(wcslen(wvalue)); + int vallen = WideCharToMultiByte(CP_UTF8, 0, + wvalue, wvallen, + 0, 0, 0, 0); + if (vallen < 0) { + SVCERR << "WARNING: Unable to convert environment value to UTF-8" + << endl; + return false; + } + + char *val = new char[vallen + 1]; + (void)WideCharToMultiByte(CP_UTF8, 0, + wvalue, wvallen, + val, vallen, 0, 0); + val[vallen] = '\0'; + + value = val; + + delete[] val; + return true; + +#else + + char *val = getenv(variable.c_str()); + if (!val) { + return false; + } + + value = val; + return true; + +#endif +} + +bool +putEnvUtf8(std::string variable, std::string value) +{ +#ifdef _WIN32 + std::string entry = variable + "=" + value; + + int wentlen = MultiByteToWideChar(CP_UTF8, 0, + entry.c_str(), int(entry.length()), + 0, 0); + if (wentlen < 0) { + SVCERR << "WARNING: Unable to convert environment entry to " + << "wide characters" << endl; + return false; + } + + wchar_t *wentbuf = new wchar_t[wentlen + 1]; + (void)MultiByteToWideChar(CP_UTF8, 0, + entry.c_str(), int(entry.length()), + wentbuf, wentlen); + wentbuf[wentlen] = L'\0'; + + int rv = _wputenv(wentbuf); + + delete[] wentbuf; + + if (rv != 0) { + SVCERR << "WARNING: Failed to set environment entry" << endl; + return false; + } + return true; + +#else + + int rv = setenv(variable.c_str(), value.c_str(), 1); + if (rv != 0) { + SVCERR << "WARNING: Failed to set environment entry" << endl; + return false; + } + return true; + +#endif +} + diff -r 85e9b7b31a8d -r 7459f4c4d7c3 system/System.h --- a/system/System.h Thu May 24 16:30:55 2018 +0100 +++ b/system/System.h Mon Jun 11 14:40:09 2018 +0100 @@ -4,7 +4,7 @@ Sonic Visualiser An audio file viewer and annotation editor. Centre for Digital Music, Queen Mary, University of London. - This file copyright 2006 Chris Cannam and QMUL. + This file copyright 2006-2018 Chris Cannam and QMUL. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -13,8 +13,8 @@ COPYING included with this distribution for more information. */ -#ifndef _SYSTEM_H_ -#define _SYSTEM_H_ +#ifndef SV_SYSTEM_H +#define SV_SYSTEM_H #include "base/Debug.h" @@ -182,6 +182,18 @@ #define powf pow #endif +/** Return the value of the given environment variable by reference. + Return true if successfully retrieved, false if unset or on error. + Both the variable name and the returned value are UTF-8 encoded. +*/ +extern bool getEnvUtf8(std::string variable, std::string &value); + +/** Set the value of the given environment variable. + Return true if successfully set, false on error. + Both the variable name and the value must be UTF-8 encoded. +*/ +extern bool putEnvUtf8(std::string variable, std::string value); + #endif /* ! _SYSTEM_H_ */ diff -r 85e9b7b31a8d -r 7459f4c4d7c3 system/test/TestEnv.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/system/test/TestEnv.h Mon Jun 11 14:40:09 2018 +0100 @@ -0,0 +1,107 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Sonic Visualiser + An audio file viewer and annotation editor. + Centre for Digital Music, Queen Mary, University of London. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#ifndef TEST_ENV_H +#define TEST_ENV_H + +#include "../System.h" + +#include +#include +#include + +#include + +using namespace std; + +const std::string utf8_name_sprkt = "\343\202\271\343\203\235\343\203\274\343\202\257\343\201\256\345\257\272\351\231\242"; + +class TestEnv : public QObject +{ + Q_OBJECT + +private slots: + void getAbsent() + { + string value = "blather"; + bool rv = getEnvUtf8("nonexistent_environment_variable_I_sincerely_hope_including_a_missspellling_just_to_be_sure", + value); + QCOMPARE(rv, false); + QCOMPARE(value, ""); + } + + void getExpected() + { + string value; + bool rv = getEnvUtf8("PATH", value); + QCOMPARE(rv, true); + QVERIFY(value != ""); + QVERIFY(value.size() > 5); // Not quite but nearly certain, + // and weeds out an unfortunate + // case where we accidentally + // returned the variable's name + // instead of its value! + } + + void roundTripAsciiAscii() + { + bool rv = false; + rv = putEnvUtf8("SV_CORE_TEST_SYSTEM_RT_A_A", "EXPECTED_VALUE"); + QCOMPARE(rv, true); + string value; + rv = getEnvUtf8("SV_CORE_TEST_SYSTEM_RT_A_A", value); + QCOMPARE(rv, true); + QCOMPARE(value, "EXPECTED_VALUE"); + } + + void roundTripAsciiUtf8() + { + bool rv = false; + rv = putEnvUtf8("SV_CORE_TEST_SYSTEM_RT_A_U", utf8_name_sprkt); + QCOMPARE(rv, true); + string value; + rv = getEnvUtf8("SV_CORE_TEST_SYSTEM_RT_A_U", value); + QCOMPARE(rv, true); + QCOMPARE(value, utf8_name_sprkt); + } + + void roundTripUtf8Ascii() + { + bool rv = false; + rv = putEnvUtf8("SV_CORE_TEST_SYSTEM_RT_\351\207\215\345\272\206_A", "EXPECTED_VALUE"); + QCOMPARE(rv, true); + string value; + rv = getEnvUtf8("SV_CORE_TEST_SYSTEM_RT_\351\207\215\345\272\206_A", value); + QCOMPARE(rv, true); + QCOMPARE(value, "EXPECTED_VALUE"); + } + + void roundTripUtf8Utf8() + { + bool rv = false; + rv = putEnvUtf8("SV_CORE_TEST_SYSTEM_RT_\351\207\215\345\272\206_A", utf8_name_sprkt); + QCOMPARE(rv, true); + string value; + rv = getEnvUtf8("SV_CORE_TEST_SYSTEM_RT_\351\207\215\345\272\206_A", value); + QCOMPARE(rv, true); + QCOMPARE(value, utf8_name_sprkt); + } +}; + +#endif + + + + + diff -r 85e9b7b31a8d -r 7459f4c4d7c3 system/test/files.pri --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/system/test/files.pri Mon Jun 11 14:40:09 2018 +0100 @@ -0,0 +1,5 @@ +TEST_HEADERS = \ + TestEnv.h + +TEST_SOURCES += \ + svcore-system-test.cpp diff -r 85e9b7b31a8d -r 7459f4c4d7c3 system/test/svcore-system-test.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/system/test/svcore-system-test.cpp Mon Jun 11 14:40:09 2018 +0100 @@ -0,0 +1,41 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ +/* + Sonic Visualiser + An audio file viewer and annotation editor. + Centre for Digital Music, Queen Mary, University of London. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#include "TestEnv.h" + +#include + +#include + +int main(int argc, char *argv[]) +{ + int good = 0, bad = 0; + + QCoreApplication app(argc, argv); + app.setOrganizationName("sonic-visualiser"); + app.setApplicationName("test-svcore-system"); + + { + TestEnv t; + if (QTest::qExec(&t, argc, argv) == 0) ++good; + else ++bad; + } + + if (bad > 0) { + SVCERR << "\n********* " << bad << " test suite(s) failed!\n" << endl; + return 1; + } else { + SVCERR << "All tests passed" << endl; + return 0; + } +}