From a0ede2e51983f3fadd3162d96b397756b892854c Mon Sep 17 00:00:00 2001
From: Francois Bleibel <fbleibel@gmail.com>
Date: Mon, 4 Oct 2010 16:04:36 +0900
Subject: [PATCH] Added import functions for scripts to dynamic-graph (from
 Thomas Moulard)

---
 include/CMakeLists.txt                        |   7 +
 .../import-default-paths.h.cmake              |  28 ++
 include/dynamic-graph/import.h                |  66 +++++
 src/CMakeLists.txt                            |   1 +
 src/dgraph/import.cpp                         | 265 ++++++++++++++++++
 src/dgraph/interpreter.cpp                    |   4 +
 6 files changed, 371 insertions(+)
 create mode 100644 include/dynamic-graph/import-default-paths.h.cmake
 create mode 100644 include/dynamic-graph/import.h
 create mode 100644 src/dgraph/import.cpp

diff --git a/include/CMakeLists.txt b/include/CMakeLists.txt
index 80c21da..6a8d219 100644
--- a/include/CMakeLists.txt
+++ b/include/CMakeLists.txt
@@ -1,3 +1,8 @@
+# Generate header with default script directory.
+SET(DG_IMPORT_DEFAULT_PATHS \"${CMAKE_INSTALL_PREFIX}/script\")
+CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_NAME}/import-default-paths.h.cmake
+		       ${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_NAME}/import-default-paths.h)
+
 SET(${PROJECT_NAME}_HEADERS
 contiifstream.h
 debug.h
@@ -10,6 +15,8 @@ interpreter.h
 interpreter-helper.h
 plugin-loader.h
 pool.h
+import.h
+import-default-paths.h
 
 exception-abstract.h
 exception-factory.h
diff --git a/include/dynamic-graph/import-default-paths.h.cmake b/include/dynamic-graph/import-default-paths.h.cmake
new file mode 100644
index 0000000..5b26156
--- /dev/null
+++ b/include/dynamic-graph/import-default-paths.h.cmake
@@ -0,0 +1,28 @@
+/* -*- c++ -*-
+ *
+ * Copyright 2010,
+ * François Bleibel,
+ * Olivier Stasse,
+ *
+ * CNRS/AIST
+ *
+ * This file is part of dynamic-graph.
+ * dynamic-graph is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ * dynamic-graph is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.  You should
+ * have received a copy of the GNU Lesser General Public License along
+ * with dynamic-graph.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef DG_FACTORY_COMMAND_IMPORT_DEFAULT_PATHS_H
+# define DG_FACTORY_COMMAND_IMPORT_DEFAULT_PATHS_H
+
+/// Default script path as known by CMake at configure time.
+# define DG_IMPORT_DEFAULT_PATHS @DG_IMPORT_DEFAULT_PATHS@
+
+#endif //! SOT_FACTORY_COMMAND_IMPORT_DEFAULT_PATHS_H
diff --git a/include/dynamic-graph/import.h b/include/dynamic-graph/import.h
new file mode 100644
index 0000000..3491d00
--- /dev/null
+++ b/include/dynamic-graph/import.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2010,
+ * François Bleibel,
+ * Olivier Stasse,
+ *
+ * CNRS/AIST
+ *
+ * This file is part of sot-core.
+ * sot-core is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ * sot-core is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.  You should
+ * have received a copy of the GNU Lesser General Public License along
+ * with sot-core.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+// -*- c++ -*-
+#ifndef SOT_FACTORY_COMMAND_IMPORT_H
+# define SOT_FACTORY_COMMAND_IMPORT_H
+# include <iosfwd>
+# include <string>
+# include <vector>
+
+namespace dynamicgraph
+{
+  class Interpreter;
+  namespace command
+  {
+    namespace
+    {
+      extern std::vector<std::string> importPaths;
+    } // end of anonymous namespace.
+
+    /// \brief Implement sot interpretor import command.
+    ///
+    /// The import command sources a file and searches automatically
+    /// for it in the importPaths.
+    void import (Interpreter& interpretor,
+		 const std::string& cmdLine,
+		 std::istringstream& cmdArg,
+		 std::ostream& os);
+
+    /// \brief Implement sot interpretor pushImportPaths command.
+    ///
+    /// Append a path to importPaths.
+    void pushImportPaths (Interpreter& interpretor,
+			  const std::string& cmdLine,
+			  std::istringstream& cmdArg,
+			  std::ostream& os);
+
+    /// \brief Implement sot interpretor popImportPaths command.
+    ///
+    /// Drop the last path of importPaths.
+    void popImportPaths (Interpreter& interpretor,
+			 const std::string& cmdLine,
+			 std::istringstream& cmdArg,
+			 std::ostream& os);
+
+  } // end of namespace command.
+} // end of namespace sot.
+
+#endif //! SOT_FACTORY_COMMAND_IMPORT_H
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 77f7d96..26c3c3f 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -23,6 +23,7 @@ ADD_LIBRARY(${LIBRARY_NAME}
   dgraph/interpreter-helper.cpp
   dgraph/plugin-loader.cpp
   dgraph/pool.cpp
+  dgraph/import.cpp
 
   exception/exception-abstract.cpp
   exception/exception-factory.cpp
diff --git a/src/dgraph/import.cpp b/src/dgraph/import.cpp
new file mode 100644
index 0000000..111d7b5
--- /dev/null
+++ b/src/dgraph/import.cpp
@@ -0,0 +1,265 @@
+/*
+ * Copyright 2010,
+ * François Bleibel,
+ * Olivier Stasse,
+ *
+ * CNRS/AIST
+ *
+ * This file is part of sot-core.
+ * sot-core is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ * sot-core is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.  You should
+ * have received a copy of the GNU Lesser General Public License along
+ * with sot-core.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <cassert>
+
+#include <iostream>
+#include <fstream>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include <boost/algorithm/string.hpp>
+#include <boost/foreach.hpp>
+#include <boost/format.hpp>
+
+#include <dynamic-graph/import-default-paths.h>
+#include <dynamic-graph/import.h>
+#include <dynamic-graph/debug.h>
+#include <dynamic-graph/exception-abstract.h>
+#include <dynamic-graph/exception-factory.h>
+#include <dynamic-graph/interpreter.h>
+
+static const char* ENV_DG_PATH = "DG_PATH";
+
+namespace dynamicgraph
+{
+  namespace command
+  {
+    namespace
+    {
+      /// Initialize import paths list (called during static initialization).
+      std::vector<std::string> initializePaths ();
+
+      /// \brief Import paths list.
+      ///
+      /// This vector of string is similar to Unix variables such as
+      /// PATH. It contains all paths that are used to search when
+      /// importing a script.
+      ///
+      /// The look-up is made from right to left:
+      ///
+      /// importPaths = A:B:C
+      ///
+      /// When typing ``import foo'', C will be searched first then B
+      /// and A. The search stops when the file is found.
+      std::vector<std::string> importPaths = initializePaths ();
+
+      /// Search for a module.
+      ///
+      /// Returns the module full absolute path or an empty string if
+      /// it cannot be found.
+      std::string searchModule (const std::string& module);
+
+      /// \brief Remove quotes form a string.
+      ///
+      /// Transform strings such as "foo" or 'foo' into foo.
+      void removeQuotes (std::string& msg);
+
+      std::vector<std::string> initializePaths ()
+      {
+	std::vector<std::string> importPaths;
+	importPaths.push_back (DG_IMPORT_DEFAULT_PATHS);
+
+	// Search for the environment variable value.
+	char* ScriptPath = getenv (ENV_DG_PATH);
+	if (!ScriptPath)
+	  return importPaths;
+
+	// Split the environment variable.
+	std::string environmentSeparator;
+
+	// On Microsoft Windows, environment variables are splitted along
+	// the ``;'' character. It is ``:'' on *NIX systems.
+#if defined _WIN32 || defined __CYGWIN__
+	environmentSeparator = ";";
+#else
+	environmentSeparator = ":";
+#endif // defined _WIN32 || defined __CYGWIN__
+
+	std::vector<std::string> splittedEnvironmentVariable;
+        boost::split (splittedEnvironmentVariable, ScriptPath,
+		      boost::is_any_of (environmentSeparator));
+
+        // Insert it back.
+        std::back_insert_iterator<std::vector<std::string> > bi (importPaths);
+        std::copy (splittedEnvironmentVariable.begin (),
+		   splittedEnvironmentVariable.end (), bi);
+        return importPaths;
+      }
+
+      std::string searchModule (const std::string& module)
+      {
+	// Make sure the traversal is right to left to enforce
+	// correct priorities.
+	typedef std::vector<std::string>::const_reverse_iterator citer_t;
+	for (citer_t it = importPaths.rbegin ();
+	     it != importPaths.rend (); ++it)
+	  {
+	    const std::string& path = *it;
+
+	    assert (!path.empty ());
+
+	    std::string filename (path);
+	    if (filename[filename.length () - 1] != '/')
+	      filename += "/";
+	    filename += module;
+	    std::ifstream file (filename.c_str ());
+	    if (file.is_open () && file.good ())
+	      return filename;
+	  }
+	return std::string ();
+      }
+
+      void removeQuotes (std::string& msg)
+      {
+	if ((msg[0] == '"' && msg[msg.length () - 1] == '"')
+	    || (msg[0] == '\'' && msg[msg.length () - 1] == '\''))
+	  msg = msg.substr (1, msg.length () - 2);
+      }
+    } // end of anonymous namespace.
+
+    void import (dynamicgraph::Interpreter& interpreter,
+		 const std::string& cmdLine,
+		 std::istringstream& cmdArg,
+		 std::ostream& os)
+    {
+      if (cmdLine == "help")
+	{
+	  os << "  - import <script.txt>\t\t\t\tImport the script."
+	     << std::endl
+	     << "\t\t\t\tBehaves like run but searches for files"
+	     << " in default script directories."
+	     << std::endl;
+	  return;
+	}
+
+      dgDEBUGIN(15);
+
+      std::string module;
+      cmdArg >> module;
+
+      // Get rid of quotes.
+      removeQuotes (module);
+
+      std::string filename = searchModule (module);
+      std::ifstream file (filename.c_str ());
+      if (filename.empty () || !file.is_open () || !file.good ())
+	{
+	  std::string scriptDirectories;
+
+	  if (importPaths.empty ())
+	    scriptDirectories = "empty";
+	  else
+	    {
+	      BOOST_FOREACH (const std::string& path, importPaths)
+		{
+		  scriptDirectories += path;
+		  scriptDirectories += ", ";
+		}
+	      scriptDirectories = scriptDirectories.substr
+		(0, scriptDirectories.length () - 2);
+	    }
+
+	  boost::format fmt
+	    ("failed to import module ``%1%'' (import paths: %2%).");
+	  fmt % module;
+	  fmt % scriptDirectories;
+	  DG_THROW ExceptionFactory
+	    (ExceptionFactory::READ_FILE, fmt.str ());
+	  return;
+	}
+
+      int lineIdx = 0;
+      try
+	{
+	  while (file.good ())
+	    {
+	      ++lineIdx;
+	      dgDEBUGIN (15);
+
+	      std::string line;
+	      std::getline (file, line);
+	      if (line.empty ())
+		continue;
+
+	      std::istringstream iss (line);
+	      std::string currentCmdName;
+	      std::string currentCmdArgs;
+	      if (iss >> currentCmdName)
+		{
+		  std::getline (iss, currentCmdArgs);
+		  boost::format fmt ("Run ``%1%'' with args ``%2%''");
+		  fmt % currentCmdName % currentCmdArgs;
+		  dgDEBUG(25) << fmt.str () << std::endl;
+		  std::istringstream issArgs (currentCmdArgs);
+		  interpreter.cmd (currentCmdName, issArgs, os);
+		}
+	      dgDEBUGOUT (15);
+	    }
+	}
+      catch (ExceptionAbstract& exc)
+	{
+	  // FIXME: come on...
+	  std::string& msg = const_cast<std::string&> (exc.getStringMessage ());
+	  boost::format fmt (" (in line %1% of file ``%2%'')");
+	  fmt % lineIdx % filename;
+	  msg = msg + fmt.str();
+	  throw;
+	}
+
+      dgDEBUGOUT(15);
+    }
+
+    void pushImportPaths (dynamicgraph::Interpreter& interpreter,
+			  const std::string& cmdLine,
+			  std::istringstream& cmdArg,
+			  std::ostream& os)
+    {
+      if (cmdLine == "help")
+	{
+	  os << "  - pushImportPaths <path>\t\t\t\tAdd path to default directories."
+	     << std::endl;
+	  return;
+	}
+      std::string path;
+      cmdArg >> path;
+      removeQuotes (path);
+
+      importPaths.push_back (path);
+    }
+
+    void popImportPaths (dynamicgraph::Interpreter& interpreter,
+			 const std::string& cmdLine,
+			 std::istringstream& cmdArg,
+			 std::ostream& os)
+    {
+      if (cmdLine == "help")
+	{
+	  os << "  - popImportPaths <path>\t\t\t\tDrop path from default directories."
+	     << std::endl;
+	  return;
+	}
+      if (!importPaths.empty ())
+	importPaths.pop_back ();
+    }
+
+  } // end of namespace command.
+} // end of namespace sot.
diff --git a/src/dgraph/interpreter.cpp b/src/dgraph/interpreter.cpp
index 781c144..d4aa0ae 100644
--- a/src/dgraph/interpreter.cpp
+++ b/src/dgraph/interpreter.cpp
@@ -26,6 +26,7 @@
 #include <dynamic-graph/interpreter.h>
 #include <dynamic-graph/plugin-loader.h>
 #include <dynamic-graph/debug.h>
+#include <dynamic-graph/import.h>
 
 /* --- STD --- */
 using namespace std;
@@ -51,6 +52,9 @@ Interpreter( PluginLoader* dl__ )
   registerFunction("set",boost::bind(&Interpreter::cmdSetSignal,this,_1,_2,_3));
   registerFunction("get",boost::bind(&Interpreter::cmdGetSignal,this,_1,_2,_3));
   registerFunction("compute",boost::bind(&Interpreter::cmdComputeSignal,this,_1,_2,_3));
+  registerFunction("import",boost::bind(&dynamicgraph::command::import,boost::ref(*this),_1,_2,_3));
+  registerFunction("pushImportPaths",boost::bind(&dynamicgraph::command::pushImportPaths,boost::ref(*this),_1,_2,_3));
+  registerFunction("popImportPaths",boost::bind(&dynamicgraph::command::popImportPaths,boost::ref(*this),_1,_2,_3));
   prompt = PROMPT_DEFAULT;
   initDone = true;
 }
-- 
GitLab