annotate src/vamp-hostsdk/Files.cpp @ 472:79a219ba6178

Correct use of multibyte-widechar conversion routines
author Chris Cannam
date Fri, 28 Oct 2016 14:30:26 +0100
parents 35fa4733bc5d
children 0545cd3f1738
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@390 69 return listLibraryFilesMatching("");
Chris@390 70 }
Chris@390 71
Chris@390 72 vector<string>
Chris@390 73 Files::listLibraryFilesMatching(string libraryName)
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@421 79 // characters (this string is expected to be utf-8)
Chris@390 80 for (size_t i = 0; i < libraryName.length(); ++i) {
Chris@421 81 if (!(libraryName[i] & 0x80)) {
Chris@421 82 libraryName[i] = char(tolower(libraryName[i]));
Chris@421 83 }
Chris@390 84 }
Chris@390 85
Chris@390 86 for (size_t i = 0; i < path.size(); ++i) {
Chris@390 87
Chris@390 88 vector<string> files = listFiles(path[i], PLUGIN_SUFFIX);
Chris@390 89
Chris@390 90 for (vector<string>::iterator fi = files.begin();
Chris@390 91 fi != files.end(); ++fi) {
Chris@390 92
Chris@390 93 if (libraryName != "") {
Chris@421 94 // we match case-insensitively, but only with ascii
Chris@421 95 // range characters (this string is expected to be
Chris@421 96 // utf-8)
Chris@390 97 string temp = *fi;
Chris@390 98 for (size_t i = 0; i < temp.length(); ++i) {
Chris@421 99 if (!(temp[i] & 0x80)) {
Chris@421 100 temp[i] = char(tolower(temp[i]));
Chris@421 101 }
Chris@390 102 }
Chris@390 103 // libraryName should be lacking an extension, as it
Chris@390 104 // is supposed to have come from the plugin key
Chris@390 105 string::size_type pi = temp.find('.');
Chris@390 106 if (pi == string::npos) {
Chris@390 107 if (libraryName != temp) continue;
Chris@390 108 } else {
Chris@390 109 if (libraryName != temp.substr(0, pi)) continue;
Chris@390 110 }
Chris@390 111 }
Chris@390 112
Chris@390 113 string fullPath = path[i];
Chris@390 114 fullPath = splicePath(fullPath, *fi);
Chris@390 115 libraryFiles.push_back(fullPath);
Chris@390 116 }
Chris@390 117 }
Chris@390 118
Chris@390 119 return libraryFiles;
Chris@390 120 }
Chris@390 121
Chris@390 122 void *
Chris@390 123 Files::loadLibrary(string path)
Chris@390 124 {
Chris@390 125 void *handle = 0;
Chris@390 126 #ifdef _WIN32
Chris@390 127 #ifdef UNICODE
Chris@472 128 int wlen = MultiByteToWideChar(CP_UTF8, 0, path.c_str(), path.length(), 0, 0);
Chris@472 129 if (wlen < 0) {
Chris@390 130 cerr << "Vamp::HostExt: Unable to convert library path \""
Chris@390 131 << path << "\" to wide characters " << endl;
Chris@390 132 return handle;
Chris@390 133 }
Chris@472 134 wchar_t *buffer = new wchar_t[wlen+1];
Chris@472 135 (void)MultiByteToWideChar(CP_UTF8, 0, path.c_str(), path.length(), buffer, wlen);
Chris@472 136 buffer[wlen] = L'\0';
Chris@390 137 handle = LoadLibrary(buffer);
Chris@390 138 delete[] buffer;
Chris@390 139 #else
Chris@390 140 handle = LoadLibrary(path.c_str());
Chris@390 141 #endif
Chris@390 142 if (!handle) {
Chris@390 143 cerr << "Vamp::HostExt: Unable to load library \""
Chris@390 144 << path << "\"" << endl;
Chris@390 145 }
Chris@390 146 #else
Chris@390 147 handle = dlopen(path.c_str(), RTLD_LAZY | RTLD_LOCAL);
Chris@390 148 if (!handle) {
Chris@390 149 cerr << "Vamp::HostExt: Unable to load library \""
Chris@390 150 << path << "\": " << dlerror() << endl;
Chris@390 151 }
Chris@390 152 #endif
Chris@390 153 return handle;
Chris@390 154 }
Chris@390 155
Chris@390 156 void
Chris@390 157 Files::unloadLibrary(void *handle)
Chris@390 158 {
Chris@390 159 #ifdef _WIN32
Chris@390 160 FreeLibrary((HINSTANCE)handle);
Chris@390 161 #else
Chris@390 162 dlclose(handle);
Chris@390 163 #endif
Chris@390 164 }
Chris@390 165
Chris@390 166 void *
Chris@390 167 Files::lookupInLibrary(void *handle, const char *symbol)
Chris@390 168 {
Chris@390 169 #ifdef _WIN32
Chris@390 170 return (void *)GetProcAddress((HINSTANCE)handle, symbol);
Chris@390 171 #else
Chris@390 172 return (void *)dlsym(handle, symbol);
Chris@390 173 #endif
Chris@390 174 }
Chris@390 175
Chris@390 176 string
Chris@390 177 Files::lcBasename(string path)
Chris@390 178 {
Chris@390 179 string basename(path);
Chris@390 180
Chris@390 181 string::size_type li = basename.rfind('/');
Chris@390 182 if (li != string::npos) basename = basename.substr(li + 1);
Chris@390 183
Chris@403 184 #ifdef _WIN32
Chris@405 185 li = basename.rfind('\\');
Chris@403 186 if (li != string::npos) basename = basename.substr(li + 1);
Chris@403 187 #endif
Chris@403 188
Chris@390 189 li = basename.find('.');
Chris@390 190 if (li != string::npos) basename = basename.substr(0, li);
Chris@390 191
Chris@421 192 // case-insensitive, but only with ascii range characters (this
Chris@421 193 // string is expected to be utf-8)
Chris@390 194 for (size_t i = 0; i < basename.length(); ++i) {
Chris@421 195 if (!(basename[i] & 0x80)) {
Chris@421 196 basename[i] = char(tolower(basename[i]));
Chris@421 197 }
Chris@390 198 }
Chris@390 199
Chris@390 200 return basename;
Chris@390 201 }
Chris@390 202
Chris@390 203 string
Chris@390 204 Files::splicePath(string a, string b)
Chris@390 205 {
Chris@390 206 #ifdef _WIN32
Chris@390 207 return a + "\\" + b;
Chris@390 208 #else
Chris@390 209 return a + "/" + b;
Chris@390 210 #endif
Chris@390 211 }
Chris@390 212
Chris@390 213 vector<string>
Chris@390 214 Files::listFiles(string dir, string extension)
Chris@390 215 {
Chris@390 216 vector<string> files;
Chris@390 217
Chris@390 218 #ifdef _WIN32
Chris@390 219 string expression = dir + "\\*." + extension;
Chris@390 220 #ifdef UNICODE
Chris@472 221 int wlen = MultiByteToWideChar(CP_UTF8, 0, expression.c_str(), expression.length(), 0, 0);
Chris@472 222 if (wlen < 0) {
Chris@390 223 cerr << "Vamp::HostExt: Unable to convert wildcard path \""
Chris@390 224 << expression << "\" to wide characters" << endl;
Chris@390 225 return files;
Chris@390 226 }
Chris@472 227 wchar_t *buffer = new wchar_t[wlen+1];
Chris@472 228 (void)MultiByteToWideChar(CP_UTF8, 0, expression.c_str(), expression.length(), buffer, wlen);
Chris@472 229 buffer[wlen] = L'\0';
Chris@390 230 WIN32_FIND_DATA data;
Chris@390 231 HANDLE fh = FindFirstFile(buffer, &data);
Chris@390 232 if (fh == INVALID_HANDLE_VALUE) {
Chris@390 233 delete[] buffer;
Chris@390 234 return files;
Chris@390 235 }
Chris@390 236
Chris@390 237 bool ok = true;
Chris@390 238 while (ok) {
Chris@390 239 wchar_t *fn = data.cFileName;
Chris@472 240 int wlen = wcslen(fn);
Chris@472 241 int len = WideCharToMultiByte(CP_UTF8, 0, fn, wlen, 0, 0, 0, 0);
Chris@472 242 if (len < 0) {
Chris@472 243 cerr << "Vamp::HostExt: Unable to convert wide char filename to utf-8" << endl;
Chris@472 244 break;
Chris@472 245 }
Chris@472 246 char *conv = new char[len+1];
Chris@472 247 (void)WideCharToMultiByte(CP_UTF8, 0, fn, wlen, conv, len, 0, 0);
Chris@472 248 conv[len] = '\0';
Chris@472 249 if (len > 0) {
Chris@390 250 files.push_back(conv);
Chris@390 251 }
Chris@390 252 delete[] conv;
Chris@390 253 ok = FindNextFile(fh, &data);
Chris@390 254 }
Chris@390 255
Chris@390 256 FindClose(fh);
Chris@390 257 delete[] buffer;
Chris@390 258 #else
Chris@390 259 WIN32_FIND_DATA data;
Chris@390 260 HANDLE fh = FindFirstFile(expression.c_str(), &data);
Chris@390 261 if (fh == INVALID_HANDLE_VALUE) return files;
Chris@390 262
Chris@390 263 bool ok = true;
Chris@390 264 while (ok) {
Chris@390 265 files.push_back(data.cFileName);
Chris@390 266 ok = FindNextFile(fh, &data);
Chris@390 267 }
Chris@390 268
Chris@390 269 FindClose(fh);
Chris@390 270 #endif
Chris@390 271 #else
Chris@390 272
Chris@390 273 size_t extlen = extension.length();
Chris@390 274 DIR *d = opendir(dir.c_str());
Chris@390 275 if (!d) return files;
Chris@390 276
Chris@390 277 struct dirent *e = 0;
Chris@390 278 while ((e = readdir(d))) {
Chris@390 279
Chris@390 280 size_t len = strlen(e->d_name);
Chris@390 281 if (len < extlen + 2 ||
Chris@390 282 e->d_name + len - extlen - 1 != "." + extension) {
Chris@390 283 continue;
Chris@390 284 }
Chris@390 285
Chris@390 286 files.push_back(e->d_name);
Chris@390 287 }
Chris@390 288
Chris@390 289 closedir(d);
Chris@390 290 #endif
Chris@390 291
Chris@390 292 return files;
Chris@390 293 }