annotate src/vamp-hostsdk/Files.cpp @ 482:95b7404af6ce

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