From: Leonardo Florez-Valencia <florez-l@javeriana.edu.co>
Date: Mon, 16 May 2016 12:57:25 +0000 (-0500)
Subject: ...
X-Git-Tag: v0.1~166
X-Git-Url: https://git.creatis.insa-lyon.fr/pubgit/?a=commitdiff_plain;h=010f4aa9d69c30818fad4c998bbcb99b4f42f43d;p=cpPlugins.git

...
---

diff --git a/appli/bash/cpPlugins_HostCreator.cxx b/appli/bash/cpPlugins_HostCreator.cxx
index eb529bd..9ed9ec8 100644
--- a/appli/bash/cpPlugins_HostCreator.cxx
+++ b/appli/bash/cpPlugins_HostCreator.cxx
@@ -75,19 +75,20 @@ void process_header( TInfo& info, const std::string& file_name )
 // -------------------------------------------------------------------------
 int main( int argc, char* argv[] )
 {
-  if( argc < 3 )
+  if( argc < 4 )
   {
     std::cerr
       << "Usage: " << argv[ 0 ]
-      << " output_file header_file_0.h header_file_0.h ..."
+      << " plugins_name output_file header_file_0.h header_file_0.h ..."
       << std::endl;
     return( 1 );
 
   } // fi
+  std::string plugins_name = argv[ 1 ];
 
   // Parse all header files
   TInfo info;
-  for( int i = 2; i < argc; ++i )
+  for( int i = 3; i < argc; ++i )
     process_header( info, argv[ i ] );
   if( info.size( ) == 0 )
   {
@@ -97,10 +98,10 @@ int main( int argc, char* argv[] )
   } // fi
 
   // Write data
-  std::ofstream out_stream( argv[ 1 ] );
+  std::ofstream out_stream( argv[ 2 ] );
 
   // Write include section
-  for( int i = 2; i < argc; ++i )
+  for( int i = 3; i < argc; ++i )
     out_stream << "#include \"" << argv[ i ] << "\"" <<std::endl;
 
   // Write init/finish section
@@ -119,6 +120,14 @@ int main( int argc, char* argv[] )
   std::string export_prefix = "__attribute__((visibility(\"default\")))";
 #endif // defined( WIN32 ) || defined( _WIN32 ) || defined( __WIN32__ )
 
+  // Write plugins name function
+  out_stream
+    << std::endl
+    << "extern \"C\" " << export_prefix << " const char* "
+    << "cpPlugins_Name( )" << std::endl << "{" << std::endl
+    << "  return( \"" << plugins_name << "\" );" << std::endl
+    << "}" << std::endl << std::endl;
+
   // Write access function
   out_stream
     << std::endl
diff --git a/appli/examples/plugins/CMakeLists.txt b/appli/examples/plugins/CMakeLists.txt
index 0408f3c..0712ef1 100644
--- a/appli/examples/plugins/CMakeLists.txt
+++ b/appli/examples/plugins/CMakeLists.txt
@@ -1,6 +1,7 @@
 SET(
   examples_SOURCES
   example_GuessPlugins
+  example_LoadPlugins
   example_LoadPluginsFile
   example_LoadPluginsDirectory
   example_ReadWriteImage
diff --git a/appli/examples/plugins/example_LoadPlugins.cxx b/appli/examples/plugins/example_LoadPlugins.cxx
new file mode 100644
index 0000000..21c61c0
--- /dev/null
+++ b/appli/examples/plugins/example_LoadPlugins.cxx
@@ -0,0 +1,49 @@
+#include <iostream>
+#include <cpPlugins/Interface.h>
+
+int main( int argc, char* argv[] )
+{
+  if( argc < 2 )
+  {
+    std::cerr << "Usage: " << argv[ 0 ] << " plugins" << std::endl;
+    return( 1 );
+
+  } // fi
+
+  // Load interface
+  cpPlugins::Interface interface;
+  try
+  {
+    interface.LoadPlugin( argv[ 1 ] );
+  }
+  catch( std::exception& err )
+  {
+    std::cerr
+      << "Error caught: "
+      << err.what( )
+      << std::endl;
+    return( 1 );
+
+  } // yrt
+
+  // Show loaded plugins
+  auto plugins = interface.GetPlugins( );
+  for( auto pIt = plugins.begin( ); pIt != plugins.end( ); ++pIt )
+    std::cout << "Plugin: " << *pIt << std::endl;
+  std::cout << std::endl;
+
+  // Show loaded filters
+  auto filters = interface.GetFilters( );
+  for( auto cIt = filters.begin( ); cIt != filters.end( ); ++cIt )
+  {
+    std::cout << "Category: " << cIt->first << std::endl;
+    for( auto nIt = cIt->second.begin( ); nIt != cIt->second.end( ); ++nIt )
+      std::cout
+        << "\tFilter: " << *nIt
+        << std::endl;
+
+  } // rof
+  return( 0 );
+}
+
+// eof - $RCSfile$
diff --git a/appli/examples/plugins/example_LoadPluginsDirectory.cxx b/appli/examples/plugins/example_LoadPluginsDirectory.cxx
index 4e9366d..803d390 100644
--- a/appli/examples/plugins/example_LoadPluginsDirectory.cxx
+++ b/appli/examples/plugins/example_LoadPluginsDirectory.cxx
@@ -23,6 +23,12 @@ int main( int argc, char* argv[] )
 
   } // yrt
 
+  // Show loaded plugins
+  auto plugins = interface.GetPlugins( );
+  for( auto pIt = plugins.begin( ); pIt != plugins.end( ); ++pIt )
+    std::cout << "Plugin: " << *pIt << std::endl;
+  std::cout << std::endl;
+
   // Show loaded filters
   auto filters = interface.GetFilters( );
   for( auto cIt = filters.begin( ); cIt != filters.end( ); ++cIt )
diff --git a/appli/examples/plugins/example_LoadPluginsFile.cxx b/appli/examples/plugins/example_LoadPluginsFile.cxx
index b9a2817..929a48c 100644
--- a/appli/examples/plugins/example_LoadPluginsFile.cxx
+++ b/appli/examples/plugins/example_LoadPluginsFile.cxx
@@ -26,6 +26,12 @@ int main( int argc, char* argv[] )
 
   } // yrt
 
+  // Show loaded plugins
+  auto plugins = interface.GetPlugins( );
+  for( auto pIt = plugins.begin( ); pIt != plugins.end( ); ++pIt )
+    std::cout << "Plugin: " << *pIt << std::endl;
+  std::cout << std::endl;
+
   // Show loaded filters
   auto filters = interface.GetFilters( );
   for( auto cIt = filters.begin( ); cIt != filters.end( ); ++cIt )
diff --git a/appli/examples/plugins/example_ReadWriteImageWithWorkspace.cxx b/appli/examples/plugins/example_ReadWriteImageWithWorkspace.cxx
index 1d2382d..f4231f9 100644
--- a/appli/examples/plugins/example_ReadWriteImageWithWorkspace.cxx
+++ b/appli/examples/plugins/example_ReadWriteImageWithWorkspace.cxx
@@ -8,7 +8,7 @@ int main( int argc, char* argv[] )
   {
     std::cerr
       << "Usage: " << argv[ 0 ]
-      << " input_image(s) output_image" << std::endl;
+      << " input_image(s) output_image [output_workspace]" << std::endl;
     return( 1 );
 
   } // fi
diff --git a/cmake/cpPlugins_Functions.cmake b/cmake/cpPlugins_Functions.cmake
index 68c2b88..d53d0f0 100644
--- a/cmake/cpPlugins_Functions.cmake
+++ b/cmake/cpPlugins_Functions.cmake
@@ -39,7 +39,7 @@ SET(host ${CMAKE_CURRENT_BINARY_DIR}/${libname}_Host.cxx)
 ADD_CUSTOM_COMMAND(
   OUTPUT ${host}
   DEPENDS ${cpPlugins_HostCreator_APP} ${headers}
-  COMMAND ${cpPlugins_HostCreator_APP} ${host} ${headers}
+  COMMAND ${cpPlugins_HostCreator_APP} ${libname} ${host} ${headers}
   )
 SET(qtsources)
 FOREACH(qth ${qtheaders})
diff --git a/lib/cpPlugins/Interface.cxx b/lib/cpPlugins/Interface.cxx
index 53b7b11..1f0a015 100644
--- a/lib/cpPlugins/Interface.cxx
+++ b/lib/cpPlugins/Interface.cxx
@@ -12,6 +12,7 @@
 cpPlugins::Interface::
 Interface( )
 {
+  this->UpdatePaths( );
 }
 
 // -------------------------------------------------------------------------
@@ -31,74 +32,101 @@ GetFilters( )
 
 // -------------------------------------------------------------------------
 void cpPlugins::Interface::
-GuessAccesiblePlugins( )
+UpdatePaths( )
 {
   // Load environment configuration
-  char* path = std::getenv( "cpPlugins_PATHS" );
-  if( path != NULL )
-  {
-    std::vector< std::string > tokens;
-    cpPlugins::TokenizeString( tokens, path, "#" );
-    for( auto tIt = tokens.begin( ); tIt != tokens.end( ); ++tIt )
-      try { this->LoadPluginDir( *tIt ); } catch( ... ) { }
-
-  } // fi
+  this->m_Paths.clear( );
+  char* p = std::getenv( "cpPlugins_PATHS" );
+  if( p != NULL )
+    cpPlugins::TokenizeString( this->m_Paths, p, cpPlugins_SEPARATOR );
+  this->m_Paths.push_back( "." );
+}
 
-  // Load local path
-  auto lpath = cpPlugins::CanonicalPath( "." );
-  try { this->LoadPluginDir( lpath ); } catch( ... ) { }
+// -------------------------------------------------------------------------
+void cpPlugins::Interface::
+GuessAccesiblePlugins( )
+{
+  for( auto i = this->m_Paths.begin( ); i != this->m_Paths.end( ); ++i )
+    try { this->LoadPluginDir( *i ); } catch( ... ) { }
 }
 
 // -------------------------------------------------------------------------
-bool cpPlugins::Interface::
-LoadConfiguration( const std::string& filename )
+void cpPlugins::Interface::
+LoadPlugin( const std::string& name )
 {
-  std::ifstream in( filename.c_str( ) );
-  if( !in )
-    return( false );
+  std::stringstream str;
+  str << cpPlugins_LIB_PREFIX << name << "." << cpPlugins_LIB_EXT;
+  std::string base_name = str.str( );
+  bool found = false;
+  for( auto i = this->m_Paths.begin( ); i != this->m_Paths.end( ); ++i )
+  {
+    std::string filename = *i;
+    if( i->back( ) != '/' )
+      filename += std::string( "/" );
+    filename += base_name;
+    try
+    {
+      this->LoadPluginFile( filename );
+      found = true;
+    }
+    catch( ... )
+    {
+    } // yrt
 
-  this->UnloadAll( );
-  std::string line;
-  while( std::getline( in, line ) )
-    try { this->LoadPluginFile( line ); } catch( ... ) { }
-  return( true );
+  } // rof
+  if( !found )
+    throw std::runtime_error(
+      std::string( "cpPlugins::Interface: Plugins library \"" ) +
+      name +
+      std::string( "\" not found." )
+      );
 }
 
 // -------------------------------------------------------------------------
-bool cpPlugins::Interface::
-SaveConfiguration( const std::string& filename ) const
+void cpPlugins::Interface::
+LoadPluginDir( const std::string& dirname )
 {
-  std::ofstream out( filename.c_str( ) );
-  if( !out )
-    return( false );
-  auto dIt = this->m_DynLibraries.begin( );
-  for( ; dIt != this->m_DynLibraries.end( ); ++dIt )
-    out << dIt->first << std::endl;
-  out.close( );
-  return( true );
+  DIR* dir;
+  struct dirent* ent;
+  if( ( dir = opendir( dirname.c_str( ) ) ) != NULL )
+  {
+    while( ( ent = readdir( dir ) ) != NULL )
+    {
+      try
+      {
+        this->LoadPluginFile(
+          dirname +
+          std::string( "/" ) +
+          ent->d_name
+          );
+      }
+      catch( ... ) { }
+
+    } // elihw
+    closedir( dir );
+  }
+  else
+    throw std::runtime_error(
+      std::string( "cpPlugins::Interface: Could not load directory " ) +
+      std::string( "\"" ) +  dirname + std::string( "\"" )
+      );
 }
 
 // -------------------------------------------------------------------------
 void cpPlugins::Interface::
 LoadPluginFile( const std::string& filename )
 {
-  // Open library with its canonical path name
-  auto canonical_fn = cpPlugins::PathHelper::CanonicalPath( filename );
-  if( canonical_fn == "" )
+  // Canonical filename
+  auto canonical = cpPlugins::PathHelper::CanonicalPath( filename );
+  if( canonical == "" )
     throw std::runtime_error(
       std::string( "cpPlugins::Interface: Library \"" ) +
       filename +
       std::string( "\" does not exist." )
       );
 
-  // Check if it was already loaded
-  if(
-    this->m_DynLibraries.find( canonical_fn ) != this->m_DynLibraries.end( )
-    )
-    return;
-
-  // Ok, try to load the library
-  void* hnd = Self::_DLOpen( canonical_fn );
+  // Try to load the library
+  void* hnd = Self::_DLOpen( canonical );
   if( hnd == NULL )
     throw std::runtime_error(
       std::string( "cpPlugins::Interface: Could not load library \"" ) +
@@ -106,6 +134,13 @@ LoadPluginFile( const std::string& filename )
       std::string( "\"" )
       );
 
+  // Get plugin name
+  std::string pl_name = Self::_DLGetName( hnd );
+
+  // Check if it was already loaded
+  if( this->m_DynLibraries.find( pl_name ) != this->m_DynLibraries.end( ) )
+    return;
+
   // Load filters
   TFilters filters = Self::_DLGetFilters( hnd );
 
@@ -134,7 +169,7 @@ LoadPluginFile( const std::string& filename )
         if( creator != NULL )
         {
           this->m_DynFilters[ catIt->first][ *clsIt ] =
-            TDynFunc( canonical_fn, creator );
+            TDynFunc( pl_name, creator );
           this->m_Filters[ catIt->first ].insert( *clsIt );
           save_handler = true;
 
@@ -148,44 +183,11 @@ LoadPluginFile( const std::string& filename )
 
   // Keep dynlib handler, if needed
   if( save_handler )
-    this->m_DynLibraries[ canonical_fn ] = hnd;
+    this->m_DynLibraries[ pl_name ] = TDynFileInfo( canonical, hnd );
   else
     Self::_DLClose( hnd );
 }
 
-// -------------------------------------------------------------------------
-unsigned int cpPlugins::Interface::
-LoadPluginDir( const std::string& dirname )
-{
-  DIR* dir;
-  struct dirent* ent;
-  unsigned int count = 0;
-  if( ( dir = opendir( dirname.c_str( ) ) ) != NULL )
-  {
-    while( ( ent = readdir( dir ) ) != NULL )
-    {
-      try
-      {
-        this->LoadPluginFile(
-          dirname +
-          std::string( "/" ) +
-          ent->d_name
-          );
-        count++;
-      }
-      catch( ... ) { }
-
-    } // elihw
-    closedir( dir );
-  }
-  else
-    throw std::runtime_error(
-      std::string( "cpPlugins::Interface: Could not load directory " ) +
-      std::string( "\"" ) +  dirname + std::string( "\"" )
-      );
-  return( count );
-}
-
 // -------------------------------------------------------------------------
 void cpPlugins::Interface::
 UnloadAll( )
@@ -195,7 +197,7 @@ UnloadAll( )
     d != this->m_DynLibraries.end( );
     ++d
     )
-    Self::_DLClose( d->second );
+    Self::_DLClose( d->second.second );
   this->m_DynLibraries.clear( );
   this->m_DynFilters.clear( );
   this->m_Filters.clear( );
@@ -220,6 +222,33 @@ Create( const std::string& category, const std::string& name )
   return( filter );
 }
 
+// -------------------------------------------------------------------------
+std::string cpPlugins::Interface::
+GetPluginName( const std::string& category, const std::string& name ) const
+{
+  std::string plugin = "";
+  auto catIt = this->m_DynFilters.find( category );
+  if( catIt != this->m_DynFilters.end( ) )
+  {
+    auto clsIt = catIt->second.find( name );
+    if( clsIt != catIt->second.end( ) )
+      plugin = clsIt->second.first;
+
+  } // fi
+  return( plugin );
+}
+
+// -------------------------------------------------------------------------
+std::vector< std::string > cpPlugins::Interface::
+GetPlugins( ) const
+{
+  std::vector< std::string > res;
+  auto i = this->m_DynLibraries.begin( );
+  for( ; i != this->m_DynLibraries.end( ); ++i )
+    res.push_back( i->first );
+  return( res );
+}
+
 // -------------------------------------------------------------------------
 void* cpPlugins::Interface::
 _DLOpen( const std::string& fname )
@@ -234,6 +263,29 @@ _DLOpen( const std::string& fname )
   return( hnd );
 }
 
+// -------------------------------------------------------------------------
+const char* cpPlugins::Interface::
+_DLGetName( void* hnd )
+{
+  // Get descriptors
+  typedef const char* ( *f_t )( );
+  f_t f = NULL;
+#ifdef cpPlugins_SYS_WINDOWS
+  f = ( f_t )( ::GetProcAddress( ( HMODULE )hnd, "cpPlugins_Name" ) );
+#else // cpPlugins_SYS_WINDOWS
+  f = ( f_t )( dlsym( hnd, "cpPlugins_Name" ) );
+#endif // cpPlugins_SYS_WINDOWS
+  if( f == NULL )
+  {
+    Self::_DLClose( hnd );
+    throw std::runtime_error(
+      "cpPlugins::Interface: Library not recognized as a cpPlugins library."
+      );
+
+  } // fi
+  return( f( ) );
+}
+
 // -------------------------------------------------------------------------
 cpPlugins::Interface::
 TFilters cpPlugins::Interface::
@@ -251,7 +303,7 @@ _DLGetFilters( void* hnd )
   {
     Self::_DLClose( hnd );
     throw std::runtime_error(
-      "cpPlugins::Interface: Library not recognized as a cpPlugins library: "
+      "cpPlugins::Interface: Library not recognized as a cpPlugins library."
       );
 
   } // fi
diff --git a/lib/cpPlugins/Interface.h b/lib/cpPlugins/Interface.h
index 22d4811..655bbfa 100644
--- a/lib/cpPlugins/Interface.h
+++ b/lib/cpPlugins/Interface.h
@@ -20,8 +20,9 @@ namespace cpPlugins
     typedef std::pair< std::string, TCreator >               TDynFunc;
     typedef std::map< std::string, TDynFunc >                TDynFilter;
     typedef std::map< std::string, TDynFilter >              TDynFilters;
-    typedef std::map< std::string, void* >                   TDynLibraries;
     typedef std::map< std::string, std::set< std::string > > TFilters;
+    typedef std::pair< std::string, void* >                  TDynFileInfo;
+    typedef std::map< std::string, TDynFileInfo >            TDynLibraries;
 
   public:
     Interface( );
@@ -29,20 +30,24 @@ namespace cpPlugins
 
     const TFilters& GetFilters( );
 
+    void UpdatePaths( );
     void GuessAccesiblePlugins( );
-
-    bool LoadConfiguration( const std::string& filename );
-    bool SaveConfiguration( const std::string& filename ) const;
+    void LoadPlugin( const std::string& name );
+    void LoadPluginDir( const std::string& dirname );
     void LoadPluginFile( const std::string& filename );
-    unsigned int LoadPluginDir( const std::string& dirname );
     void UnloadAll( );
 
     cpPlugins::ProcessObject::Pointer Create(
       const std::string& category, const std::string& name
       );
+    std::string GetPluginName(
+      const std::string& category, const std::string& name
+      ) const;
+    std::vector< std::string > GetPlugins( ) const;
 
   protected:
     static void* _DLOpen( const std::string& fname );
+    static const char* _DLGetName( void* hnd );
     static TFilters _DLGetFilters( void* hnd );
     static TCreator _DLGetCreator(
       void* hnd, const std::string& category, const std::string& name
@@ -50,6 +55,7 @@ namespace cpPlugins
     static void _DLClose( void* hnd );
 
   protected:
+    std::vector< std::string > m_Paths;
     TDynLibraries m_DynLibraries;
     TDynFilters   m_DynFilters;
     TFilters      m_Filters;
diff --git a/lib/cpPlugins/Workspace.cxx b/lib/cpPlugins/Workspace.cxx
index d749829..d441c49 100644
--- a/lib/cpPlugins/Workspace.cxx
+++ b/lib/cpPlugins/Workspace.cxx
@@ -118,8 +118,7 @@ CreateFilter(
   // Get or create new filter from name
   if( !( this->m_Graph->HasVertexIndex( name ) ) )
   {
-    ProcessObject::Pointer f =
-      this->m_Interface->Create( category, filter );
+    ProcessObject::Pointer f = this->m_Interface->Create( category, filter );
     if( f.IsNotNull( ) )
     {
       if( f->IsInteractive( ) )
diff --git a/lib/cpPlugins_Config.h b/lib/cpPlugins_Config.h
index e157cd3..9886262 100644
--- a/lib/cpPlugins_Config.h
+++ b/lib/cpPlugins_Config.h
@@ -34,6 +34,7 @@
 #  define cpPlugins_SYS_WINDOWS
 #  define cpPlugins_LIB_PREFIX ""
 #  define cpPlugins_LIB_EXT "dll"
+#  define cpPlugins_SEPARATOR ";"
 #  ifndef WIN32_LEAN_AND_MEAN
 #    define WIN32_LEAN_AND_MEAN
 #  endif
@@ -44,14 +45,17 @@
 #  define cpPlugins_SYS_LINUX
 #  define cpPlugins_LIB_PREFIX "lib"
 #  define cpPlugins_LIB_EXT "so"
+#  define cpPlugins_SEPARATOR ":"
 #elif defined( __APPLE__ ) || defined( MACOSX ) || defined( macintosh ) || defined( Macintosh )
 #  define cpPlugins_SYS_MACOS
 #  define cpPlugins_LIB_PREFIX "lib"
 #  define cpPlugins_LIB_EXT "dylib"
+#  define cpPlugins_SEPARATOR ":"
 #elif defined( __FreeBSD__ ) || defined( __FreeBSD_kernel__ )
 #  define cpPlugins_SYS_FREEBSD
 #  define cpPlugins_LIB_PREFIX "lib"
 #  define cpPlugins_LIB_EXT "so"
+#  define cpPlugins_SEPARATOR ":"
 #else
 #  error "This operating system is not supported by cpPlugins"
 #endif
diff --git a/third_party_installers/cpPlugins_Install_CMAKE.sh b/third_party_installers/cpPlugins_Install_CMAKE.sh
index 07cc1a8..ed6256f 100755
--- a/third_party_installers/cpPlugins_Install_CMAKE.sh
+++ b/third_party_installers/cpPlugins_Install_CMAKE.sh
@@ -141,6 +141,11 @@ echo "==> Platform          : $platform"
 echo "==> Number of cores   : $number_of_cores"
 echo "==> Number of threads : $number_of_threads"
 echo "====================================================================="
+read -n1 -r -p "Continue? [Y/N]... " key
+echo
+if [ "$key" != 'Y' -a "$key" != 'y' ] ; then
+    exit 1
+fi
 
 ## Create paths
 if [ "x$source_file" != "x" ]; then
diff --git a/third_party_installers/cpPlugins_Install_ITK.sh b/third_party_installers/cpPlugins_Install_ITK.sh
index 4a97e9e..99dcd85 100755
--- a/third_party_installers/cpPlugins_Install_ITK.sh
+++ b/third_party_installers/cpPlugins_Install_ITK.sh
@@ -148,6 +148,11 @@ echo "==> Platform          : $platform"
 echo "==> Number of cores   : $number_of_cores"
 echo "==> Number of threads : $number_of_threads"
 echo "====================================================================="
+read -n1 -r -p "Continue? [Y/N]... " key
+echo
+if [ "$key" != 'Y' -a "$key" != 'y' ] ; then
+    exit 1
+fi
 
 ## Create paths
 if [ "x$source_file" != "x" ]; then
diff --git a/third_party_installers/cpPlugins_Install_QT4.sh b/third_party_installers/cpPlugins_Install_QT4.sh
index 0960129..36c5d13 100755
--- a/third_party_installers/cpPlugins_Install_QT4.sh
+++ b/third_party_installers/cpPlugins_Install_QT4.sh
@@ -136,6 +136,11 @@ echo "==> Number of cores   : $number_of_cores"
 echo "==> Number of threads : $number_of_threads"
 echo "==> Patch file        : $patch_file"
 echo "====================================================================="
+read -n1 -r -p "Continue? [Y/N]... " key
+echo
+if [ "$key" != 'Y' -a "$key" != 'y' ] ; then
+    exit 1
+fi
 
 ## Create paths
 if [ "x$source_file" != "x" ]; then
diff --git a/third_party_installers/cpPlugins_Install_VTK.sh b/third_party_installers/cpPlugins_Install_VTK.sh
index 06f6501..1f8ecc3 100755
--- a/third_party_installers/cpPlugins_Install_VTK.sh
+++ b/third_party_installers/cpPlugins_Install_VTK.sh
@@ -174,6 +174,11 @@ echo "==> Platform          : $platform"
 echo "==> Number of cores   : $number_of_cores"
 echo "==> Number of threads : $number_of_threads"
 echo "====================================================================="
+read -n1 -r -p "Continue? [Y/N]... " key
+echo
+if [ "$key" != 'Y' -a "$key" != 'y' ] ; then
+    exit 1
+fi
 
 ## Create paths
 if [ "x$source_file" != "x" ]; then