annotate src/vamp-hostsdk/Files.cpp @ 525:8c18bdaad04f c++11-mutex

Avoid simple static allocation of mutex, as it could lead to mutex being destroyed before last adapter that needs to use it (since adapters are usually also static)
author Chris Cannam
date Mon, 09 Sep 2019 10:24:13 +0100
parents da86fb0bccb3
children 45b2bd15d8ae
rev   line source
Chris@390 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@390 2
Chris@390 3 /*
Chris@390 4 Vamp
Chris@390 5
Chris@390 6 An API for audio analysis and feature extraction plugins.
Chris@390 7
Chris@390 8 Centre for Digital Music, Queen Mary, University of London.
Chris@390 9 Copyright 2006-2015 Chris Cannam and QMUL.
Chris@390 10
Chris@390 11 Permission is hereby granted, free of charge, to any person
Chris@390 12 obtaining a copy of this software and associated documentation
Chris@390 13 files (the "Software"), to deal in the Software without
Chris@390 14 restriction, including without limitation the rights to use, copy,
Chris@390 15 modify, merge, publish, distribute, sublicense, and/or sell copies
Chris@390 16 of the Software, and to permit persons to whom the Software is
Chris@390 17 furnished to do so, subject to the following conditions:
Chris@390 18
Chris@390 19 The above copyright notice and this permission notice shall be
Chris@390 20 included in all copies or substantial portions of the Software.
Chris@390 21
Chris@390 22 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
Chris@390 23 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
Chris@390 24 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
Chris@390 25 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
Chris@390 26 ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
Chris@390 27 CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
Chris@390 28 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Chris@390 29
Chris@390 30 Except as contained in this notice, the names of the Centre for
Chris@390 31 Digital Music; Queen Mary, University of London; and Chris Cannam
Chris@390 32 shall not be used in advertising or otherwise to promote the sale,
Chris@390 33 use or other dealings in this Software without prior written
Chris@390 34 authorization.
Chris@390 35 */
Chris@390 36
Chris@390 37 #include <vamp-hostsdk/PluginHostAdapter.h>
Chris@390 38
Chris@390 39 #include "Files.h"
Chris@390 40
Chris@390 41 #include <cctype> // tolower
Chris@390 42
Chris@390 43 #include <cstring>
Chris@390 44
Chris@390 45 #ifdef _WIN32
Chris@390 46
Chris@390 47 #include <windows.h>
Chris@390 48 #include <tchar.h>
Chris@390 49 #define PLUGIN_SUFFIX "dll"
Chris@390 50
Chris@390 51 #else /* ! _WIN32 */
Chris@390 52
Chris@514 53 #include <cstdlib>
Chris@390 54 #include <dirent.h>
Chris@390 55 #include <dlfcn.h>
Chris@390 56
Chris@390 57 #ifdef __APPLE__
Chris@390 58 #define PLUGIN_SUFFIX "dylib"
Chris@390 59 #else /* ! __APPLE__ */
Chris@390 60 #define PLUGIN_SUFFIX "so"
Chris@390 61 #endif /* ! __APPLE__ */
Chris@390 62
Chris@390 63 #endif /* ! _WIN32 */
Chris@390 64
Chris@390 65 using namespace std;
Chris@390 66
Chris@390 67 vector<string>
Chris@390 68 Files::listLibraryFiles()
Chris@390 69 {
Chris@477 70 return listLibraryFilesMatching(Filter());
Chris@390 71 }
Chris@390 72
Chris@390 73 vector<string>
Chris@473 74 Files::listLibraryFilesMatching(Filter filter)
Chris@390 75 {
Chris@390 76 vector<string> path = Vamp::PluginHostAdapter::getPluginPath();
Chris@390 77 vector<string> libraryFiles;
Chris@390 78
Chris@421 79 // we match case-insensitively, but only with ascii range
Chris@473 80 // characters (input strings are expected to be utf-8)
Chris@473 81 vector<string> libraryNames;
Chris@477 82 for (int j = 0; j < int(filter.libraryNames.size()); ++j) {
Chris@477 83 string n = filter.libraryNames[j];
Chris@473 84 for (size_t i = 0; i < n.length(); ++i) {
Chris@473 85 if (!(n[i] & 0x80)) {
Chris@473 86 n[i] = char(tolower(n[i]));
Chris@473 87 }
Chris@421 88 }
Chris@473 89 libraryNames.push_back(n);
Chris@390 90 }
Chris@390 91
Chris@390 92 for (size_t i = 0; i < path.size(); ++i) {
Chris@390 93
Chris@390 94 vector<string> files = listFiles(path[i], PLUGIN_SUFFIX);
Chris@390 95
Chris@390 96 for (vector<string>::iterator fi = files.begin();
Chris@390 97 fi != files.end(); ++fi) {
Chris@473 98
Chris@473 99 // we match case-insensitively, but only with ascii range
Chris@473 100 // characters (this string is expected to be utf-8)
Chris@473 101 string cleaned = *fi;
Chris@477 102 for (size_t j = 0; j < cleaned.length(); ++j) {
Chris@477 103 if (!(cleaned[j] & 0x80)) {
Chris@477 104 cleaned[j] = char(tolower(cleaned[j]));
Chris@390 105 }
Chris@390 106 }
Chris@390 107
Chris@473 108 // libraryName should be lacking an extension, as it is
Chris@473 109 // supposed to have come from the plugin key
Chris@473 110 string::size_type pi = cleaned.find('.');
Chris@473 111 if (pi != string::npos) {
Chris@473 112 cleaned = cleaned.substr(0, pi);
Chris@473 113 }
Chris@473 114
Chris@473 115 bool matched = false;
Chris@473 116
Chris@473 117 switch (filter.type) {
Chris@473 118
Chris@473 119 case Filter::All:
Chris@473 120 matched = true;
Chris@473 121 break;
Chris@473 122
Chris@473 123 case Filter::Matching:
Chris@477 124 for (int j = 0; j < int(libraryNames.size()); ++j) {
Chris@477 125 if (cleaned == libraryNames[j]) {
Chris@473 126 matched = true;
Chris@473 127 break;
Chris@473 128 }
Chris@473 129 }
Chris@473 130 break;
Chris@473 131
Chris@473 132 case Filter::NotMatching:
Chris@473 133 matched = true;
Chris@477 134 for (int j = 0; j < int(libraryNames.size()); ++j) {
Chris@477 135 if (cleaned == libraryNames[j]) {
Chris@473 136 matched = false;
Chris@473 137 break;
Chris@473 138 }
Chris@473 139 }
Chris@473 140 break;
Chris@473 141 }
Chris@473 142
Chris@473 143 if (!matched) continue;
Chris@473 144
Chris@390 145 string fullPath = path[i];
Chris@390 146 fullPath = splicePath(fullPath, *fi);
Chris@390 147 libraryFiles.push_back(fullPath);
Chris@390 148 }
Chris@390 149 }
Chris@390 150
Chris@390 151 return libraryFiles;
Chris@390 152 }
Chris@390 153
Chris@390 154 void *
Chris@390 155 Files::loadLibrary(string path)
Chris@390 156 {
Chris@390 157 void *handle = 0;
Chris@390 158 #ifdef _WIN32
Chris@390 159 #ifdef UNICODE
Chris@472 160 int wlen = MultiByteToWideChar(CP_UTF8, 0, path.c_str(), path.length(), 0, 0);
Chris@472 161 if (wlen < 0) {
Chris@390 162 cerr << "Vamp::HostExt: Unable to convert library path \""
Chris@390 163 << path << "\" to wide characters " << endl;
Chris@390 164 return handle;
Chris@390 165 }
Chris@472 166 wchar_t *buffer = new wchar_t[wlen+1];
Chris@472 167 (void)MultiByteToWideChar(CP_UTF8, 0, path.c_str(), path.length(), buffer, wlen);
Chris@472 168 buffer[wlen] = L'\0';
Chris@390 169 handle = LoadLibrary(buffer);
Chris@390 170 delete[] buffer;
Chris@390 171 #else
Chris@390 172 handle = LoadLibrary(path.c_str());
Chris@390 173 #endif
Chris@390 174 if (!handle) {
Chris@390 175 cerr << "Vamp::HostExt: Unable to load library \""
Chris@390 176 << path << "\"" << endl;
Chris@390 177 }
Chris@390 178 #else
Chris@390 179 handle = dlopen(path.c_str(), RTLD_LAZY | RTLD_LOCAL);
Chris@390 180 if (!handle) {
Chris@390 181 cerr << "Vamp::HostExt: Unable to load library \""
Chris@390 182 << path << "\": " << dlerror() << endl;
Chris@390 183 }
Chris@390 184 #endif
Chris@390 185 return handle;
Chris@390 186 }
Chris@390 187
Chris@390 188 void
Chris@390 189 Files::unloadLibrary(void *handle)
Chris@390 190 {
Chris@390 191 #ifdef _WIN32
Chris@390 192 FreeLibrary((HINSTANCE)handle);
Chris@390 193 #else
Chris@390 194 dlclose(handle);
Chris@390 195 #endif
Chris@390 196 }
Chris@390 197
Chris@390 198 void *
Chris@390 199 Files::lookupInLibrary(void *handle, const char *symbol)
Chris@390 200 {
Chris@390 201 #ifdef _WIN32
Chris@390 202 return (void *)GetProcAddress((HINSTANCE)handle, symbol);
Chris@390 203 #else
Chris@390 204 return (void *)dlsym(handle, symbol);
Chris@390 205 #endif
Chris@390 206 }
Chris@390 207
Chris@390 208 string
Chris@390 209 Files::lcBasename(string path)
Chris@390 210 {
Chris@390 211 string basename(path);
Chris@390 212
Chris@390 213 string::size_type li = basename.rfind('/');
Chris@390 214 if (li != string::npos) basename = basename.substr(li + 1);
Chris@390 215
Chris@403 216 #ifdef _WIN32
Chris@405 217 li = basename.rfind('\\');
Chris@403 218 if (li != string::npos) basename = basename.substr(li + 1);
Chris@403 219 #endif
Chris@403 220
Chris@390 221 li = basename.find('.');
Chris@390 222 if (li != string::npos) basename = basename.substr(0, li);
Chris@390 223
Chris@421 224 // case-insensitive, but only with ascii range characters (this
Chris@421 225 // string is expected to be utf-8)
Chris@390 226 for (size_t i = 0; i < basename.length(); ++i) {
Chris@421 227 if (!(basename[i] & 0x80)) {
Chris@421 228 basename[i] = char(tolower(basename[i]));
Chris@421 229 }
Chris@390 230 }
Chris@390 231
Chris@390 232 return basename;
Chris@390 233 }
Chris@390 234
Chris@390 235 string
Chris@390 236 Files::splicePath(string a, string b)
Chris@390 237 {
Chris@390 238 #ifdef _WIN32
Chris@390 239 return a + "\\" + b;
Chris@390 240 #else
Chris@390 241 return a + "/" + b;
Chris@390 242 #endif
Chris@390 243 }
Chris@390 244
Chris@390 245 vector<string>
Chris@390 246 Files::listFiles(string dir, string extension)
Chris@390 247 {
Chris@390 248 vector<string> files;
Chris@390 249
Chris@390 250 #ifdef _WIN32
Chris@390 251 string expression = dir + "\\*." + extension;
Chris@390 252 #ifdef UNICODE
Chris@472 253 int wlen = MultiByteToWideChar(CP_UTF8, 0, expression.c_str(), expression.length(), 0, 0);
Chris@472 254 if (wlen < 0) {
Chris@390 255 cerr << "Vamp::HostExt: Unable to convert wildcard path \""
Chris@390 256 << expression << "\" to wide characters" << endl;
Chris@390 257 return files;
Chris@390 258 }
Chris@472 259 wchar_t *buffer = new wchar_t[wlen+1];
Chris@472 260 (void)MultiByteToWideChar(CP_UTF8, 0, expression.c_str(), expression.length(), buffer, wlen);
Chris@472 261 buffer[wlen] = L'\0';
Chris@390 262 WIN32_FIND_DATA data;
Chris@390 263 HANDLE fh = FindFirstFile(buffer, &data);
Chris@390 264 if (fh == INVALID_HANDLE_VALUE) {
Chris@390 265 delete[] buffer;
Chris@390 266 return files;
Chris@390 267 }
Chris@390 268
Chris@390 269 bool ok = true;
Chris@390 270 while (ok) {
Chris@390 271 wchar_t *fn = data.cFileName;
Chris@472 272 int wlen = wcslen(fn);
Chris@472 273 int len = WideCharToMultiByte(CP_UTF8, 0, fn, wlen, 0, 0, 0, 0);
Chris@472 274 if (len < 0) {
Chris@472 275 cerr << "Vamp::HostExt: Unable to convert wide char filename to utf-8" << endl;
Chris@472 276 break;
Chris@472 277 }
Chris@472 278 char *conv = new char[len+1];
Chris@472 279 (void)WideCharToMultiByte(CP_UTF8, 0, fn, wlen, conv, len, 0, 0);
Chris@472 280 conv[len] = '\0';
Chris@472 281 if (len > 0) {
Chris@390 282 files.push_back(conv);
Chris@390 283 }
Chris@390 284 delete[] conv;
Chris@390 285 ok = FindNextFile(fh, &data);
Chris@390 286 }
Chris@390 287
Chris@390 288 FindClose(fh);
Chris@390 289 delete[] buffer;
Chris@390 290 #else
Chris@390 291 WIN32_FIND_DATA data;
Chris@390 292 HANDLE fh = FindFirstFile(expression.c_str(), &data);
Chris@390 293 if (fh == INVALID_HANDLE_VALUE) return files;
Chris@390 294
Chris@390 295 bool ok = true;
Chris@390 296 while (ok) {
Chris@390 297 files.push_back(data.cFileName);
Chris@390 298 ok = FindNextFile(fh, &data);
Chris@390 299 }
Chris@390 300
Chris@390 301 FindClose(fh);
Chris@390 302 #endif
Chris@390 303 #else
Chris@390 304
Chris@390 305 size_t extlen = extension.length();
Chris@390 306 DIR *d = opendir(dir.c_str());
Chris@390 307 if (!d) return files;
Chris@390 308
Chris@390 309 struct dirent *e = 0;
Chris@390 310 while ((e = readdir(d))) {
Chris@390 311
Chris@390 312 size_t len = strlen(e->d_name);
Chris@390 313 if (len < extlen + 2 ||
Chris@390 314 e->d_name + len - extlen - 1 != "." + extension) {
Chris@390 315 continue;
Chris@390 316 }
Chris@390 317
Chris@390 318 files.push_back(e->d_name);
Chris@390 319 }
Chris@390 320
Chris@390 321 closedir(d);
Chris@390 322 #endif
Chris@390 323
Chris@390 324 return files;
Chris@390 325 }
Chris@512 326
Chris@512 327 bool
Chris@513 328 Files::isNonNative32Bit()
Chris@513 329 {
Chris@513 330 // Return true if we are running on a system for which we should
Chris@513 331 // use the VAMP_PATH_32 variable instead of VAMP_PATH. This will
Chris@513 332 // be the case if we are a 32-bit executable but the OS is
Chris@513 333 // natively 64-bit.
Chris@513 334 //
Chris@513 335 // This currently works only on Windows; other operating systems
Chris@513 336 // will use VAMP_PATH always.
Chris@513 337
Chris@513 338 if (sizeof(void *) == 8) {
Chris@513 339 return false;
Chris@513 340 }
Chris@513 341
Chris@513 342 #ifdef _WIN32
Chris@513 343 BOOL wow64 = FALSE;
Chris@513 344 BOOL (WINAPI *fnIsWow64Process)(HANDLE, PBOOL) =
Chris@513 345 (BOOL (WINAPI *)(HANDLE, PBOOL)) GetProcAddress
Chris@513 346 (GetModuleHandle(TEXT("kernel32")), "IsWow64Process");
Chris@513 347 if (fnIsWow64Process) {
Chris@513 348 if (fnIsWow64Process(GetCurrentProcess(), &wow64)) {
Chris@513 349 if (wow64) {
Chris@513 350 return true;
Chris@513 351 } else {
Chris@513 352 return false;
Chris@513 353 }
Chris@513 354 } else {
Chris@513 355 cerr << "Vamp::HostExt: Unable to query process architecture"
Chris@513 356 << endl;
Chris@513 357 return false;
Chris@513 358 }
Chris@513 359 } else {
Chris@513 360 cerr << "Vamp::HostExt: Unable to query process architecture: "
Chris@513 361 << "Function not available" << endl;
Chris@513 362 return false;
Chris@513 363 }
Chris@513 364 #endif
Chris@513 365
Chris@513 366 return false;
Chris@513 367 }
Chris@513 368
Chris@513 369 bool
Chris@512 370 Files::getEnvUtf8(std::string variable, std::string &value)
Chris@512 371 {
Chris@512 372 value = "";
Chris@512 373
Chris@512 374 #ifdef _WIN32
Chris@512 375 int wvarlen = MultiByteToWideChar(CP_UTF8, 0,
Chris@512 376 variable.c_str(), int(variable.length()),
Chris@512 377 0, 0);
Chris@512 378 if (wvarlen < 0) {
Chris@512 379 cerr << "Vamp::HostExt: Unable to convert environment variable name "
Chris@512 380 << variable << " to wide characters" << endl;
Chris@512 381 return false;
Chris@512 382 }
Chris@512 383
Chris@512 384 wchar_t *wvarbuf = new wchar_t[wvarlen + 1];
Chris@512 385 (void)MultiByteToWideChar(CP_UTF8, 0,
Chris@512 386 variable.c_str(), int(variable.length()),
Chris@512 387 wvarbuf, wvarlen);
Chris@512 388 wvarbuf[wvarlen] = L'\0';
Chris@512 389
Chris@512 390 wchar_t *wvalue = _wgetenv(wvarbuf);
Chris@512 391
Chris@512 392 delete[] wvarbuf;
Chris@512 393
Chris@512 394 if (!wvalue) {
Chris@512 395 return false;
Chris@512 396 }
Chris@512 397
Chris@512 398 int wvallen = int(wcslen(wvalue));
Chris@512 399 int vallen = WideCharToMultiByte(CP_UTF8, 0,
Chris@512 400 wvalue, wvallen,
Chris@512 401 0, 0, 0, 0);
Chris@512 402 if (vallen < 0) {
Chris@512 403 cerr << "Vamp::HostExt: Unable to convert environment value to UTF-8"
Chris@512 404 << endl;
Chris@512 405 return false;
Chris@512 406 }
Chris@512 407
Chris@512 408 char *val = new char[vallen + 1];
Chris@512 409 (void)WideCharToMultiByte(CP_UTF8, 0,
Chris@512 410 wvalue, wvallen,
Chris@512 411 val, vallen, 0, 0);
Chris@512 412 val[vallen] = '\0';
Chris@512 413
Chris@512 414 value = val;
Chris@512 415
Chris@512 416 delete[] val;
Chris@512 417 return true;
Chris@512 418
Chris@512 419 #else
Chris@512 420
Chris@512 421 char *val = getenv(variable.c_str());
Chris@512 422 if (!val) {
Chris@512 423 return false;
Chris@512 424 }
Chris@512 425
Chris@512 426 value = val;
Chris@512 427 return true;
Chris@512 428
Chris@512 429 #endif
Chris@512 430 }