annotate src/vamp-hostsdk/Files.cpp @ 476:15348e89c1d7

Fix fairly obscure mis-canonicalisation in RealTime (leading to non-canonical but still correct values)
author Chris Cannam
date Fri, 18 Nov 2016 12:53:21 +0000
parents 0545cd3f1738
children 628a5b8ff634
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@390 53 #include <dirent.h>
Chris@390 54 #include <dlfcn.h>
Chris@390 55
Chris@390 56 #ifdef __APPLE__
Chris@390 57 #define PLUGIN_SUFFIX "dylib"
Chris@390 58 #else /* ! __APPLE__ */
Chris@390 59 #define PLUGIN_SUFFIX "so"
Chris@390 60 #endif /* ! __APPLE__ */
Chris@390 61
Chris@390 62 #endif /* ! _WIN32 */
Chris@390 63
Chris@390 64 using namespace std;
Chris@390 65
Chris@390 66 vector<string>
Chris@390 67 Files::listLibraryFiles()
Chris@390 68 {
Chris@473 69 return listLibraryFilesMatching({});
Chris@390 70 }
Chris@390 71
Chris@390 72 vector<string>
Chris@473 73 Files::listLibraryFilesMatching(Filter filter)
Chris@390 74 {
Chris@390 75 vector<string> path = Vamp::PluginHostAdapter::getPluginPath();
Chris@390 76 vector<string> libraryFiles;
Chris@390 77
Chris@421 78 // we match case-insensitively, but only with ascii range
Chris@473 79 // characters (input strings are expected to be utf-8)
Chris@473 80 vector<string> libraryNames;
Chris@473 81 for (auto n: filter.libraryNames) {
Chris@473 82 for (size_t i = 0; i < n.length(); ++i) {
Chris@473 83 if (!(n[i] & 0x80)) {
Chris@473 84 n[i] = char(tolower(n[i]));
Chris@473 85 }
Chris@421 86 }
Chris@473 87 libraryNames.push_back(n);
Chris@390 88 }
Chris@390 89
Chris@390 90 for (size_t i = 0; i < path.size(); ++i) {
Chris@390 91
Chris@390 92 vector<string> files = listFiles(path[i], PLUGIN_SUFFIX);
Chris@390 93
Chris@390 94 for (vector<string>::iterator fi = files.begin();
Chris@390 95 fi != files.end(); ++fi) {
Chris@473 96
Chris@473 97 // we match case-insensitively, but only with ascii range
Chris@473 98 // characters (this string is expected to be utf-8)
Chris@473 99 string cleaned = *fi;
Chris@473 100 for (size_t i = 0; i < cleaned.length(); ++i) {
Chris@473 101 if (!(cleaned[i] & 0x80)) {
Chris@473 102 cleaned[i] = char(tolower(cleaned[i]));
Chris@390 103 }
Chris@390 104 }
Chris@390 105
Chris@473 106 // libraryName should be lacking an extension, as it is
Chris@473 107 // supposed to have come from the plugin key
Chris@473 108 string::size_type pi = cleaned.find('.');
Chris@473 109 if (pi != string::npos) {
Chris@473 110 cleaned = cleaned.substr(0, pi);
Chris@473 111 }
Chris@473 112
Chris@473 113 bool matched = false;
Chris@473 114
Chris@473 115 switch (filter.type) {
Chris@473 116
Chris@473 117 case Filter::All:
Chris@473 118 matched = true;
Chris@473 119 break;
Chris@473 120
Chris@473 121 case Filter::Matching:
Chris@473 122 for (const auto &n: libraryNames) {
Chris@473 123 if (cleaned == n) {
Chris@473 124 matched = true;
Chris@473 125 break;
Chris@473 126 }
Chris@473 127 }
Chris@473 128 break;
Chris@473 129
Chris@473 130 case Filter::NotMatching:
Chris@473 131 matched = true;
Chris@473 132 for (const auto &n: libraryNames) {
Chris@473 133 if (cleaned == n) {
Chris@473 134 matched = false;
Chris@473 135 break;
Chris@473 136 }
Chris@473 137 }
Chris@473 138 break;
Chris@473 139 }
Chris@473 140
Chris@473 141 if (!matched) continue;
Chris@473 142
Chris@390 143 string fullPath = path[i];
Chris@390 144 fullPath = splicePath(fullPath, *fi);
Chris@390 145 libraryFiles.push_back(fullPath);
Chris@390 146 }
Chris@390 147 }
Chris@390 148
Chris@390 149 return libraryFiles;
Chris@390 150 }
Chris@390 151
Chris@390 152 void *
Chris@390 153 Files::loadLibrary(string path)
Chris@390 154 {
Chris@390 155 void *handle = 0;
Chris@390 156 #ifdef _WIN32
Chris@390 157 #ifdef UNICODE
Chris@472 158 int wlen = MultiByteToWideChar(CP_UTF8, 0, path.c_str(), path.length(), 0, 0);
Chris@472 159 if (wlen < 0) {
Chris@390 160 cerr << "Vamp::HostExt: Unable to convert library path \""
Chris@390 161 << path << "\" to wide characters " << endl;
Chris@390 162 return handle;
Chris@390 163 }
Chris@472 164 wchar_t *buffer = new wchar_t[wlen+1];
Chris@472 165 (void)MultiByteToWideChar(CP_UTF8, 0, path.c_str(), path.length(), buffer, wlen);
Chris@472 166 buffer[wlen] = L'\0';
Chris@390 167 handle = LoadLibrary(buffer);
Chris@390 168 delete[] buffer;
Chris@390 169 #else
Chris@390 170 handle = LoadLibrary(path.c_str());
Chris@390 171 #endif
Chris@390 172 if (!handle) {
Chris@390 173 cerr << "Vamp::HostExt: Unable to load library \""
Chris@390 174 << path << "\"" << endl;
Chris@390 175 }
Chris@390 176 #else
Chris@390 177 handle = dlopen(path.c_str(), RTLD_LAZY | RTLD_LOCAL);
Chris@390 178 if (!handle) {
Chris@390 179 cerr << "Vamp::HostExt: Unable to load library \""
Chris@390 180 << path << "\": " << dlerror() << endl;
Chris@390 181 }
Chris@390 182 #endif
Chris@390 183 return handle;
Chris@390 184 }
Chris@390 185
Chris@390 186 void
Chris@390 187 Files::unloadLibrary(void *handle)
Chris@390 188 {
Chris@390 189 #ifdef _WIN32
Chris@390 190 FreeLibrary((HINSTANCE)handle);
Chris@390 191 #else
Chris@390 192 dlclose(handle);
Chris@390 193 #endif
Chris@390 194 }
Chris@390 195
Chris@390 196 void *
Chris@390 197 Files::lookupInLibrary(void *handle, const char *symbol)
Chris@390 198 {
Chris@390 199 #ifdef _WIN32
Chris@390 200 return (void *)GetProcAddress((HINSTANCE)handle, symbol);
Chris@390 201 #else
Chris@390 202 return (void *)dlsym(handle, symbol);
Chris@390 203 #endif
Chris@390 204 }
Chris@390 205
Chris@390 206 string
Chris@390 207 Files::lcBasename(string path)
Chris@390 208 {
Chris@390 209 string basename(path);
Chris@390 210
Chris@390 211 string::size_type li = basename.rfind('/');
Chris@390 212 if (li != string::npos) basename = basename.substr(li + 1);
Chris@390 213
Chris@403 214 #ifdef _WIN32
Chris@405 215 li = basename.rfind('\\');
Chris@403 216 if (li != string::npos) basename = basename.substr(li + 1);
Chris@403 217 #endif
Chris@403 218
Chris@390 219 li = basename.find('.');
Chris@390 220 if (li != string::npos) basename = basename.substr(0, li);
Chris@390 221
Chris@421 222 // case-insensitive, but only with ascii range characters (this
Chris@421 223 // string is expected to be utf-8)
Chris@390 224 for (size_t i = 0; i < basename.length(); ++i) {
Chris@421 225 if (!(basename[i] & 0x80)) {
Chris@421 226 basename[i] = char(tolower(basename[i]));
Chris@421 227 }
Chris@390 228 }
Chris@390 229
Chris@390 230 return basename;
Chris@390 231 }
Chris@390 232
Chris@390 233 string
Chris@390 234 Files::splicePath(string a, string b)
Chris@390 235 {
Chris@390 236 #ifdef _WIN32
Chris@390 237 return a + "\\" + b;
Chris@390 238 #else
Chris@390 239 return a + "/" + b;
Chris@390 240 #endif
Chris@390 241 }
Chris@390 242
Chris@390 243 vector<string>
Chris@390 244 Files::listFiles(string dir, string extension)
Chris@390 245 {
Chris@390 246 vector<string> files;
Chris@390 247
Chris@390 248 #ifdef _WIN32
Chris@390 249 string expression = dir + "\\*." + extension;
Chris@390 250 #ifdef UNICODE
Chris@472 251 int wlen = MultiByteToWideChar(CP_UTF8, 0, expression.c_str(), expression.length(), 0, 0);
Chris@472 252 if (wlen < 0) {
Chris@390 253 cerr << "Vamp::HostExt: Unable to convert wildcard path \""
Chris@390 254 << expression << "\" to wide characters" << endl;
Chris@390 255 return files;
Chris@390 256 }
Chris@472 257 wchar_t *buffer = new wchar_t[wlen+1];
Chris@472 258 (void)MultiByteToWideChar(CP_UTF8, 0, expression.c_str(), expression.length(), buffer, wlen);
Chris@472 259 buffer[wlen] = L'\0';
Chris@390 260 WIN32_FIND_DATA data;
Chris@390 261 HANDLE fh = FindFirstFile(buffer, &data);
Chris@390 262 if (fh == INVALID_HANDLE_VALUE) {
Chris@390 263 delete[] buffer;
Chris@390 264 return files;
Chris@390 265 }
Chris@390 266
Chris@390 267 bool ok = true;
Chris@390 268 while (ok) {
Chris@390 269 wchar_t *fn = data.cFileName;
Chris@472 270 int wlen = wcslen(fn);
Chris@472 271 int len = WideCharToMultiByte(CP_UTF8, 0, fn, wlen, 0, 0, 0, 0);
Chris@472 272 if (len < 0) {
Chris@472 273 cerr << "Vamp::HostExt: Unable to convert wide char filename to utf-8" << endl;
Chris@472 274 break;
Chris@472 275 }
Chris@472 276 char *conv = new char[len+1];
Chris@472 277 (void)WideCharToMultiByte(CP_UTF8, 0, fn, wlen, conv, len, 0, 0);
Chris@472 278 conv[len] = '\0';
Chris@472 279 if (len > 0) {
Chris@390 280 files.push_back(conv);
Chris@390 281 }
Chris@390 282 delete[] conv;
Chris@390 283 ok = FindNextFile(fh, &data);
Chris@390 284 }
Chris@390 285
Chris@390 286 FindClose(fh);
Chris@390 287 delete[] buffer;
Chris@390 288 #else
Chris@390 289 WIN32_FIND_DATA data;
Chris@390 290 HANDLE fh = FindFirstFile(expression.c_str(), &data);
Chris@390 291 if (fh == INVALID_HANDLE_VALUE) return files;
Chris@390 292
Chris@390 293 bool ok = true;
Chris@390 294 while (ok) {
Chris@390 295 files.push_back(data.cFileName);
Chris@390 296 ok = FindNextFile(fh, &data);
Chris@390 297 }
Chris@390 298
Chris@390 299 FindClose(fh);
Chris@390 300 #endif
Chris@390 301 #else
Chris@390 302
Chris@390 303 size_t extlen = extension.length();
Chris@390 304 DIR *d = opendir(dir.c_str());
Chris@390 305 if (!d) return files;
Chris@390 306
Chris@390 307 struct dirent *e = 0;
Chris@390 308 while ((e = readdir(d))) {
Chris@390 309
Chris@390 310 size_t len = strlen(e->d_name);
Chris@390 311 if (len < extlen + 2 ||
Chris@390 312 e->d_name + len - extlen - 1 != "." + extension) {
Chris@390 313 continue;
Chris@390 314 }
Chris@390 315
Chris@390 316 files.push_back(e->d_name);
Chris@390 317 }
Chris@390 318
Chris@390 319 closedir(d);
Chris@390 320 #endif
Chris@390 321
Chris@390 322 return files;
Chris@390 323 }