| 333 | class PluginManager { |
| 334 | public: |
| 335 | std::shared_ptr<PluginLibrary> LoadByType(const std::string& type) { |
| 336 | if (IsTruthy(std::getenv("OPENCC_DISABLE_PLUGINS"))) { |
| 337 | throw Exception("Segmentation plugin loading is disabled."); |
| 338 | } |
| 339 | |
| 340 | std::lock_guard<std::mutex> lock(mutex_); |
| 341 | auto it = cache_.find(type); |
| 342 | if (it != cache_.end()) { |
| 343 | return it->second; |
| 344 | } |
| 345 | |
| 346 | const std::vector<std::string> searchDirs = BuildSearchDirs(); |
| 347 | const std::vector<std::string> fileNames = GetPluginFileNames(type); |
| 348 | std::ostringstream attempted; |
| 349 | bool first = true; |
| 350 | for (const auto& dir : searchDirs) { |
| 351 | std::vector<std::string> matches; |
| 352 | for (const auto& fileName : fileNames) { |
| 353 | const std::string candidate = JoinPath(dir, fileName); |
| 354 | if (!first) { |
| 355 | attempted << ", "; |
| 356 | } |
| 357 | first = false; |
| 358 | attempted << candidate; |
| 359 | if (IsReadableFile(candidate)) { |
| 360 | matches.push_back(candidate); |
| 361 | } |
| 362 | } |
| 363 | |
| 364 | if (matches.size() > 1) { |
| 365 | throw Exception("Multiple segmentation plugin libraries found for '" + |
| 366 | type + "' in directory '" + dir + |
| 367 | "'. Keep only one matching DLL per plugin type."); |
| 368 | } |
| 369 | if (matches.empty()) { |
| 370 | continue; |
| 371 | } |
| 372 | try { |
| 373 | std::shared_ptr<PluginLibrary> plugin = |
| 374 | LoadFromPath(matches.front(), type); |
| 375 | cache_[type] = plugin; |
| 376 | return plugin; |
| 377 | } catch (const Exception&) { |
| 378 | } |
| 379 | } |
| 380 | throw Exception("Segmentation plugin '" + type + |
| 381 | "' not found. Searched: " + attempted.str()); |
| 382 | } |
| 383 | |
| 384 | std::shared_ptr<PluginLibrary> LoadByPath(const std::string& path, |
| 385 | const std::string& type) { |