Chris@92
|
1 /* -*- c-basic-offset: 8 indent-tabs-mode: t -*- */
|
fazekasgy@37
|
2 /*
|
fazekasgy@37
|
3
|
fazekasgy@37
|
4 * Vampy : This plugin is a wrapper around the Vamp plugin API.
|
fazekasgy@37
|
5 * It allows for writing Vamp plugins in Python.
|
fazekasgy@37
|
6
|
fazekasgy@37
|
7 * Centre for Digital Music, Queen Mary University of London.
|
fazekasgy@37
|
8 * Copyright (C) 2008-2009 Gyorgy Fazekas, QMUL. (See Vamp sources
|
fazekasgy@37
|
9 * for licence information.)
|
fazekasgy@37
|
10
|
fazekasgy@37
|
11 */
|
fazekasgy@37
|
12
|
fazekasgy@37
|
13
|
fazekasgy@37
|
14 #include "PyPlugScanner.h"
|
Chris@92
|
15 #include "PyExtensionManager.h"
|
Chris@92
|
16 #include "Debug.h"
|
fazekasgy@37
|
17 #include <algorithm>
|
fazekasgy@37
|
18 #include <cstdlib>
|
fazekasgy@37
|
19 //#include "vamp-hostsdk/PluginHostAdapter.h"
|
fazekasgy@37
|
20
|
fazekasgy@37
|
21 #ifdef _WIN32
|
Chris@120
|
22 // We should be using the unicode apis, but we're not (yet)
|
Chris@120
|
23 #undef UNICODE
|
Chris@120
|
24 #undef _UNICODE
|
Chris@120
|
25 #define _MBCS 1
|
fazekasgy@37
|
26 #include <windows.h>
|
fazekasgy@37
|
27 #include <tchar.h>
|
fazekasgy@37
|
28 #define pathsep ("\\")
|
fazekasgy@37
|
29 #else
|
fazekasgy@37
|
30 #include <dirent.h>
|
fazekasgy@37
|
31 #include <dlfcn.h>
|
fazekasgy@37
|
32 #define pathsep ("/")
|
fazekasgy@37
|
33 #endif
|
fazekasgy@37
|
34 #define joinPath(a,b) ( (a)+pathsep+(b) )
|
fazekasgy@37
|
35
|
fazekasgy@37
|
36 using std::string;
|
fazekasgy@37
|
37 using std::vector;
|
fazekasgy@37
|
38 using std::cerr;
|
fazekasgy@37
|
39 using std::endl;
|
fazekasgy@37
|
40 using std::find;
|
fazekasgy@37
|
41
|
fazekasgy@37
|
42 PyPlugScanner::PyPlugScanner()
|
fazekasgy@37
|
43 {
|
fazekasgy@37
|
44
|
fazekasgy@37
|
45 }
|
fazekasgy@37
|
46
|
fazekasgy@37
|
47 PyPlugScanner *PyPlugScanner::m_instance = NULL;
|
fazekasgy@37
|
48 bool PyPlugScanner::m_hasInstance = false;
|
fazekasgy@37
|
49
|
fazekasgy@37
|
50 PyPlugScanner*
|
fazekasgy@37
|
51 PyPlugScanner::getInstance()
|
fazekasgy@37
|
52 {
|
fazekasgy@37
|
53 if (!m_hasInstance) {
|
fazekasgy@37
|
54 m_instance = new PyPlugScanner();
|
fazekasgy@37
|
55 m_hasInstance = true;
|
fazekasgy@37
|
56 }
|
fazekasgy@37
|
57 return m_instance;
|
fazekasgy@37
|
58 }
|
fazekasgy@37
|
59
|
fazekasgy@37
|
60 void
|
fazekasgy@37
|
61 PyPlugScanner::setPath(vector<string> path)
|
fazekasgy@37
|
62 {
|
fazekasgy@37
|
63 m_path=path;
|
fazekasgy@37
|
64 }
|
fazekasgy@37
|
65
|
fazekasgy@37
|
66 // We assume that each script on the path has one valid class
|
fazekasgy@37
|
67 vector<string>
|
fazekasgy@37
|
68 PyPlugScanner::getPyPlugs()
|
fazekasgy@37
|
69 {
|
fazekasgy@37
|
70 //for_each m_path listFiles then return vector<pyPlugs>
|
fazekasgy@37
|
71 //key format: FullPathString/FileName.py:ClassName
|
fazekasgy@37
|
72
|
fazekasgy@37
|
73 bool getCompiled = true;
|
fazekasgy@37
|
74 char* getPyc = getenv("VAMPY_COMPILED");
|
fazekasgy@37
|
75 if (getPyc) {
|
fazekasgy@37
|
76 string value(getPyc);
|
fazekasgy@37
|
77 cerr << "VAMPY_COMPILED=" << value << endl;
|
fazekasgy@37
|
78 getCompiled = value.compare("1")?false:true;
|
fazekasgy@37
|
79 }
|
fazekasgy@37
|
80
|
fazekasgy@37
|
81 vector<string> pyPlugs;
|
fazekasgy@37
|
82 string pluginKey;
|
fazekasgy@37
|
83 PyObject *pyClass;
|
fazekasgy@37
|
84
|
fazekasgy@37
|
85 for (size_t i = 0; i < m_path.size(); ++i) {
|
fazekasgy@37
|
86
|
fazekasgy@37
|
87 vector<string> files = listFiles(m_path[i],"py");
|
fazekasgy@37
|
88
|
fazekasgy@37
|
89 /// recognise byte compiled plugins
|
fazekasgy@37
|
90 if (getCompiled) {
|
fazekasgy@58
|
91 vector<string> pyc_files = listFiles(m_path[i],"pyc");
|
fazekasgy@58
|
92 vector<string> pyo_files = listFiles(m_path[i],"pyo");
|
fazekasgy@58
|
93 mergeFileLists(pyc_files,pyo_files,".pyo");
|
fazekasgy@58
|
94 mergeFileLists(pyo_files,files,".py");
|
fazekasgy@37
|
95 }
|
fazekasgy@37
|
96
|
fazekasgy@37
|
97 for (vector<string>::iterator fi = files.begin();
|
fazekasgy@37
|
98 fi != files.end(); ++fi) {
|
fazekasgy@37
|
99 string script = *fi;
|
fazekasgy@37
|
100 if (!script.empty()) {
|
fazekasgy@37
|
101 string classname=script.substr(0,script.rfind('.'));
|
fazekasgy@37
|
102 pluginKey=joinPath(m_path[i],script)+":"+classname;
|
fazekasgy@37
|
103 pyClass = getScriptClass(m_path[i],classname);
|
fazekasgy@37
|
104 if (pyClass == NULL)
|
Chris@92
|
105 cerr << "Warning: Syntax error or other problem in scanning VamPy plugin: "
|
fazekasgy@37
|
106 << classname << ". Avoiding plugin." << endl;
|
fazekasgy@37
|
107 else {
|
fazekasgy@37
|
108 pyPlugs.push_back(pluginKey);
|
fazekasgy@37
|
109 m_pyClasses.push_back(pyClass);
|
Chris@92
|
110 }
|
fazekasgy@37
|
111 }
|
fazekasgy@37
|
112 }
|
fazekasgy@37
|
113 }
|
fazekasgy@37
|
114
|
fazekasgy@37
|
115 return pyPlugs;
|
fazekasgy@37
|
116
|
fazekasgy@37
|
117 }
|
fazekasgy@37
|
118
|
fazekasgy@37
|
119 /// insert python byte code names (.pyc) if a .py file can not be found
|
fazekasgy@37
|
120 /// The interpreter automatically generates byte code files and executes
|
fazekasgy@37
|
121 /// them if they exist. Therefore, we prefer .py files, but we allow
|
fazekasgy@37
|
122 /// (relatively) closed source distributions by recognising .pyc files.
|
fazekasgy@37
|
123 void
|
fazekasgy@58
|
124 PyPlugScanner::mergeFileLists(vector<string> &src, vector<string> &tg, string target_ext)
|
fazekasgy@37
|
125 {
|
fazekasgy@58
|
126 for (vector<string>::iterator srcit = src.begin();
|
fazekasgy@58
|
127 srcit != src.end(); ++srcit) {
|
fazekasgy@58
|
128 // cerr << *srcit;
|
fazekasgy@58
|
129 string src_name = *srcit;
|
fazekasgy@58
|
130 string tg_name = src_name.substr(0,src_name.rfind('.')) + target_ext;
|
fazekasgy@58
|
131 vector<string>::iterator tgit = find (tg.begin(), tg.end(), tg_name);
|
fazekasgy@58
|
132 if (tgit == tg.end()) tg.push_back(src_name);
|
fazekasgy@37
|
133 }
|
fazekasgy@37
|
134
|
fazekasgy@37
|
135 }
|
fazekasgy@37
|
136
|
fazekasgy@37
|
137
|
fazekasgy@37
|
138 //For now return one class object found in each script
|
fazekasgy@37
|
139 vector<PyObject*>
|
fazekasgy@37
|
140 PyPlugScanner::getPyClasses()
|
fazekasgy@37
|
141 {
|
fazekasgy@37
|
142 return m_pyClasses;
|
fazekasgy@37
|
143
|
fazekasgy@37
|
144 }
|
fazekasgy@37
|
145
|
fazekasgy@37
|
146 //Validate
|
fazekasgy@37
|
147 //This should not be called more than once!
|
fazekasgy@37
|
148 PyObject*
|
fazekasgy@37
|
149 PyPlugScanner::getScriptClass(string path, string classname)
|
fazekasgy@37
|
150 {
|
fazekasgy@37
|
151 //Add plugin path to active Python Path
|
Chris@108
|
152 string pyCmd =
|
Chris@108
|
153 "import sys\n"
|
Chris@108
|
154 "sys.path.append('" + path + "')\n"
|
Chris@108
|
155 "sys.argv = ['" + classname + "']\n";
|
fazekasgy@37
|
156 PyRun_SimpleString(pyCmd.c_str());
|
fazekasgy@37
|
157
|
fazekasgy@37
|
158 //Assign an object to the source code
|
fazekasgy@37
|
159 PyObject *pySource = PyString_FromString(classname.c_str());
|
fazekasgy@37
|
160
|
fazekasgy@37
|
161 //Import it as a module into the py interpreter
|
fazekasgy@37
|
162 PyObject *pyModule = PyImport_Import(pySource);
|
fazekasgy@37
|
163 PyObject* pyError = PyErr_Occurred();
|
Chris@70
|
164 if (pyError) {
|
fazekasgy@37
|
165 cerr << "ERROR: error importing source: " << classname << endl;
|
fazekasgy@37
|
166 PyErr_Print();
|
fazekasgy@37
|
167 Py_DECREF(pySource);
|
fazekasgy@37
|
168 Py_CLEAR(pyModule); // safer if pyModule==NULL
|
fazekasgy@37
|
169 return NULL;
|
fazekasgy@37
|
170 }
|
fazekasgy@37
|
171 Py_DECREF(pySource);
|
fazekasgy@37
|
172
|
fazekasgy@37
|
173 //Read the dictionary object holding the namespace of the module (borrowed reference)
|
fazekasgy@37
|
174 PyObject *pyDict = PyModule_GetDict(pyModule);
|
fazekasgy@37
|
175 Py_DECREF(pyModule);
|
fazekasgy@37
|
176
|
fazekasgy@37
|
177 //Get the PluginClass from the module (borrowed reference)
|
fazekasgy@37
|
178 PyObject *pyClass = PyDict_GetItemString(pyDict, classname.c_str());
|
fazekasgy@37
|
179
|
Chris@92
|
180 if (pyClass == Py_None) {
|
Chris@92
|
181 DSTREAM << "Vampy: class name " << classname
|
Chris@92
|
182 << " is None in module; assuming it was scrubbed "
|
Chris@92
|
183 << "following an earlier load failure" << endl;
|
Chris@92
|
184 return NULL;
|
Chris@92
|
185 }
|
Chris@92
|
186
|
Chris@92
|
187 // Check if class is present and a callable method is implemented
|
Chris@92
|
188 if (!pyClass || !PyCallable_Check(pyClass)) {
|
fazekasgy@37
|
189 cerr << "ERROR: callable plugin class could not be found in source: " << classname << endl
|
fazekasgy@37
|
190 << "Hint: plugin source filename and plugin class name must be the same." << endl;
|
fazekasgy@37
|
191 PyErr_Print();
|
fazekasgy@37
|
192 return NULL;
|
fazekasgy@37
|
193 }
|
Chris@92
|
194
|
Chris@92
|
195 bool acceptable = true;
|
Chris@92
|
196
|
Chris@92
|
197 // Check that the module doesn't have any name collisions with
|
Chris@92
|
198 // our own symbols
|
Chris@92
|
199
|
Chris@92
|
200 int i = 0;
|
Chris@92
|
201 while (PyExtensionManager::m_exposedNames[i]) {
|
Chris@92
|
202
|
Chris@92
|
203 const char* name = PyExtensionManager::m_exposedNames[i];
|
Chris@92
|
204 i++;
|
Chris@92
|
205
|
Chris@92
|
206 PyObject *item = PyDict_GetItemString(pyDict, name);
|
Chris@92
|
207 if (!item) continue;
|
Chris@92
|
208
|
Chris@92
|
209 if (item == Py_None) {
|
Chris@92
|
210 DSTREAM << "Vampy: name " << name << " is None "
|
Chris@92
|
211 << "in module " << classname
|
Chris@92
|
212 << "; assuming it was cleared on unload"
|
Chris@92
|
213 << endl;
|
Chris@92
|
214 continue;
|
Chris@92
|
215 }
|
Chris@92
|
216
|
Chris@92
|
217 PyTypeObject *metatype = Py_TYPE(item);
|
Chris@92
|
218
|
Chris@115
|
219 if (!metatype) {
|
Chris@115
|
220 cerr << "ERROR: plugin " << classname
|
Chris@115
|
221 << " reports null metatype for Vampy name \""
|
Chris@115
|
222 << name << "\" (incomplete due to load error?)"
|
Chris@115
|
223 << endl;
|
Chris@115
|
224 acceptable = false;
|
Chris@115
|
225 break;
|
Chris@115
|
226 }
|
Chris@115
|
227
|
Chris@92
|
228 if (!strcmp(name, "frame2RealTime")) {
|
Chris@92
|
229 if (metatype != &PyCFunction_Type) {
|
Chris@92
|
230 cerr << "ERROR: plugin " << classname
|
Chris@92
|
231 << " redefines Vampy function name \""
|
Chris@92
|
232 << name << "\" (metatype is \""
|
Chris@92
|
233 << metatype->tp_name << "\")" << endl;
|
Chris@92
|
234 acceptable = false;
|
Chris@92
|
235 break;
|
Chris@92
|
236 } else {
|
Chris@92
|
237 continue;
|
Chris@92
|
238 }
|
Chris@92
|
239 }
|
Chris@92
|
240
|
Chris@92
|
241 if (metatype != &PyType_Type) {
|
Chris@92
|
242 cerr << "ERROR: plugin " << classname
|
Chris@92
|
243 << " uses Vampy reserved type name \"" << name
|
Chris@92
|
244 << "\" for non-type (metatype is \""
|
Chris@92
|
245 << metatype->tp_name << "\")" << endl;
|
Chris@92
|
246 acceptable = false;
|
Chris@92
|
247 break;
|
Chris@92
|
248 }
|
Chris@92
|
249
|
Chris@92
|
250 PyTypeObject *type = (PyTypeObject *)item;
|
Chris@92
|
251 if (type->tp_name == std::string("vampy.") + name) {
|
Chris@92
|
252 DSTREAM << "Vampy: acceptable Vampy type name "
|
Chris@92
|
253 << type->tp_name << " found in module" << endl;
|
Chris@92
|
254 } else {
|
Chris@92
|
255 cerr << "ERROR: plugin " << classname
|
Chris@92
|
256 << " redefines Vampy type \"" << name << "\"";
|
Chris@92
|
257 if (strcmp(type->tp_name, name)) {
|
Chris@92
|
258 cerr << " (as \"" << type->tp_name << "\")";
|
Chris@92
|
259 }
|
Chris@92
|
260 cerr << endl;
|
Chris@92
|
261 acceptable = false;
|
Chris@92
|
262 break;
|
Chris@92
|
263 }
|
Chris@92
|
264 }
|
Chris@92
|
265
|
Chris@92
|
266 if (acceptable) {
|
Chris@92
|
267 return pyClass;
|
Chris@92
|
268 } else {
|
Chris@92
|
269 PyObject *key = PyString_FromString(classname.c_str());
|
Chris@92
|
270 PyDict_SetItem(pyDict, key, Py_None);
|
Chris@92
|
271 Py_DECREF(key);
|
Chris@92
|
272 return NULL;
|
Chris@92
|
273 }
|
fazekasgy@37
|
274 }
|
fazekasgy@37
|
275
|
fazekasgy@37
|
276
|
fazekasgy@37
|
277
|
fazekasgy@37
|
278 // Return a list of files in dir with given extension
|
fazekasgy@37
|
279 // Code taken from hostext/PluginLoader.cpp
|
fazekasgy@37
|
280 vector<string>
|
fazekasgy@37
|
281 PyPlugScanner::listFiles(string dir, string extension)
|
fazekasgy@37
|
282 {
|
fazekasgy@37
|
283 vector<string> files;
|
fazekasgy@37
|
284
|
fazekasgy@37
|
285 #ifdef _WIN32
|
fazekasgy@37
|
286
|
fazekasgy@37
|
287 string expression = dir + "\\*." + extension;
|
fazekasgy@37
|
288 WIN32_FIND_DATA data;
|
fazekasgy@37
|
289 HANDLE fh = FindFirstFile(expression.c_str(), &data);
|
fazekasgy@37
|
290 if (fh == INVALID_HANDLE_VALUE) return files;
|
fazekasgy@37
|
291
|
fazekasgy@37
|
292 bool ok = true;
|
fazekasgy@37
|
293 while (ok) {
|
fazekasgy@37
|
294 files.push_back(data.cFileName);
|
fazekasgy@37
|
295 ok = FindNextFile(fh, &data);
|
fazekasgy@37
|
296 }
|
fazekasgy@37
|
297
|
fazekasgy@37
|
298 FindClose(fh);
|
fazekasgy@37
|
299
|
fazekasgy@37
|
300 #else
|
fazekasgy@37
|
301
|
fazekasgy@37
|
302 size_t extlen = extension.length();
|
fazekasgy@37
|
303 DIR *d = opendir(dir.c_str());
|
fazekasgy@37
|
304 if (!d) return files;
|
fazekasgy@37
|
305
|
fazekasgy@37
|
306 struct dirent *e = 0;
|
fazekasgy@37
|
307 while ((e = readdir(d))) {
|
fazekasgy@37
|
308 size_t len = strlen(e->d_name);
|
fazekasgy@37
|
309 if (len < extlen + 2 ||
|
fazekasgy@37
|
310 e->d_name + len - extlen - 1 != "." + extension) {
|
fazekasgy@37
|
311 continue;
|
fazekasgy@37
|
312 }
|
fazekasgy@37
|
313 files.push_back(e->d_name);
|
fazekasgy@37
|
314 }
|
fazekasgy@37
|
315
|
fazekasgy@37
|
316 closedir(d);
|
fazekasgy@37
|
317 #endif
|
fazekasgy@37
|
318
|
fazekasgy@37
|
319 return files;
|
fazekasgy@37
|
320 }
|
fazekasgy@37
|
321
|
fazekasgy@37
|
322
|
fazekasgy@37
|
323 //!!! It would probably be better to actually call
|
fazekasgy@37
|
324 // PluginHostAdapter::getPluginPath. That would mean this "plugin"
|
fazekasgy@37
|
325 // needs to link against vamp-hostsdk, but that's probably acceptable
|
fazekasgy@37
|
326 // as it is sort of a host as well.
|
fazekasgy@37
|
327
|
fazekasgy@37
|
328 // std::vector<std::string>
|
fazekasgy@37
|
329 // PyPlugScanner::getAllValidPath()
|
fazekasgy@37
|
330 // {
|
fazekasgy@37
|
331 // Vamp::PluginHostAdapter host_adapter( ??? );
|
fazekasgy@37
|
332 // return host_adapter.getPluginPath();
|
fazekasgy@37
|
333 // }
|
fazekasgy@37
|
334
|
fazekasgy@37
|
335 // tried to implement it, but found a bit confusing how to
|
fazekasgy@37
|
336 // instantiate the host adapter here...
|
fazekasgy@37
|
337
|
fazekasgy@37
|
338
|
fazekasgy@37
|
339 //Return correct plugin directories as per platform
|
fazekasgy@37
|
340 //Code taken from vamp-sdk/PluginHostAdapter.cpp
|
fazekasgy@37
|
341 std::vector<std::string>
|
fazekasgy@37
|
342 PyPlugScanner::getAllValidPath()
|
fazekasgy@37
|
343 {
|
fazekasgy@37
|
344 std::vector<std::string> path;
|
fazekasgy@37
|
345 std::string envPath;
|
fazekasgy@37
|
346
|
Chris@97
|
347 bool nonNative32 = false;
|
Chris@97
|
348 #ifdef _WIN32
|
Chris@97
|
349 BOOL (WINAPI *fnIsWow64Process)(HANDLE, PBOOL) =
|
Chris@97
|
350 (BOOL (WINAPI *)(HANDLE, PBOOL)) GetProcAddress
|
Chris@97
|
351 (GetModuleHandle(TEXT("kernel32")), "IsWow64Process");
|
Chris@97
|
352 if (fnIsWow64Process) {
|
Chris@97
|
353 BOOL wow64 = FALSE;
|
Chris@97
|
354 if (fnIsWow64Process(GetCurrentProcess(), &wow64) && wow64) {
|
Chris@97
|
355 nonNative32 = true;
|
Chris@97
|
356 }
|
Chris@97
|
357 }
|
Chris@97
|
358 #endif
|
Chris@97
|
359
|
Chris@97
|
360 char *cpath;
|
Chris@97
|
361 if (nonNative32) {
|
Chris@97
|
362 cpath = getenv("VAMP_PATH_32");
|
Chris@97
|
363 } else {
|
Chris@97
|
364 cpath = getenv("VAMP_PATH");
|
Chris@97
|
365 }
|
fazekasgy@37
|
366 if (cpath) envPath = cpath;
|
fazekasgy@37
|
367
|
fazekasgy@37
|
368 #ifdef _WIN32
|
fazekasgy@37
|
369 #define PATH_SEPARATOR ';'
|
fazekasgy@37
|
370 #define DEFAULT_VAMP_PATH "%ProgramFiles%\\Vamp Plugins"
|
fazekasgy@37
|
371 #else
|
fazekasgy@37
|
372 #define PATH_SEPARATOR ':'
|
fazekasgy@37
|
373 #ifdef __APPLE__
|
fazekasgy@37
|
374 #define DEFAULT_VAMP_PATH "$HOME/Library/Audio/Plug-Ins/Vamp:/Library/Audio/Plug-Ins/Vamp"
|
fazekasgy@37
|
375 #else
|
fazekasgy@37
|
376 #define DEFAULT_VAMP_PATH "$HOME/vamp:$HOME/.vamp:/usr/local/lib/vamp:/usr/lib/vamp"
|
fazekasgy@37
|
377 #endif
|
fazekasgy@37
|
378 #endif
|
fazekasgy@37
|
379
|
fazekasgy@37
|
380 if (envPath == "") {
|
fazekasgy@37
|
381 envPath = DEFAULT_VAMP_PATH;
|
fazekasgy@37
|
382 char *chome = getenv("HOME");
|
fazekasgy@37
|
383 if (chome) {
|
fazekasgy@37
|
384 std::string home(chome);
|
fazekasgy@37
|
385 std::string::size_type f;
|
fazekasgy@37
|
386 while ((f = envPath.find("$HOME")) != std::string::npos &&
|
fazekasgy@37
|
387 f < envPath.length()) {
|
fazekasgy@37
|
388 envPath.replace(f, 5, home);
|
fazekasgy@37
|
389 }
|
fazekasgy@37
|
390 }
|
fazekasgy@37
|
391 #ifdef _WIN32
|
Chris@120
|
392 const char *cpfiles = getenv("ProgramFiles");
|
fazekasgy@37
|
393 if (!cpfiles) cpfiles = "C:\\Program Files";
|
fazekasgy@37
|
394 std::string pfiles(cpfiles);
|
fazekasgy@37
|
395 std::string::size_type f;
|
fazekasgy@37
|
396 while ((f = envPath.find("%ProgramFiles%")) != std::string::npos &&
|
fazekasgy@37
|
397 f < envPath.length()) {
|
fazekasgy@37
|
398 envPath.replace(f, 14, pfiles);
|
fazekasgy@37
|
399 }
|
fazekasgy@37
|
400 #endif
|
fazekasgy@37
|
401 }
|
fazekasgy@37
|
402
|
fazekasgy@37
|
403 std::string::size_type index = 0, newindex = 0;
|
fazekasgy@37
|
404
|
fazekasgy@37
|
405 while ((newindex = envPath.find(PATH_SEPARATOR, index)) < envPath.size()) {
|
fazekasgy@37
|
406 path.push_back(envPath.substr(index, newindex - index));
|
fazekasgy@37
|
407 index = newindex + 1;
|
fazekasgy@37
|
408 }
|
fazekasgy@37
|
409
|
fazekasgy@37
|
410 path.push_back(envPath.substr(index));
|
fazekasgy@37
|
411
|
fazekasgy@37
|
412 //can add an extra path for vampy plugins
|
fazekasgy@37
|
413 char* extraPath = getenv("VAMPY_EXTPATH");
|
fazekasgy@37
|
414 if (extraPath) {
|
fazekasgy@37
|
415 string vampyPath(extraPath);
|
fazekasgy@37
|
416 cerr << "VAMPY_EXTPATH=" << vampyPath << endl;
|
fazekasgy@37
|
417 path.push_back(vampyPath);
|
fazekasgy@37
|
418 }
|
fazekasgy@37
|
419
|
fazekasgy@37
|
420 return path;
|
fazekasgy@37
|
421 }
|