Merge changes Iea0902e7,Id9400625

* changes:
  Restrict 'cd' to the 'prepare' context
  Add a real path computation
diff --git a/src/ast_path.cpp b/src/ast_path.cpp
index e0348db..b34817b 100644
--- a/src/ast_path.cpp
+++ b/src/ast_path.cpp
@@ -359,3 +359,16 @@
     impl_pushFragment(m_nodes, fragment);
     validatePathNodes(m_nodes);
 }
+
+dataPath_ realPath(const dataPath_& cwd, const dataPath_& newPath)
+{
+    if (newPath.m_scope == Scope::Absolute) {
+        return newPath;
+    }
+
+    dataPath_ res = cwd;
+    for (const auto& it : newPath.m_nodes) {
+        res.pushFragment(it);
+    }
+    return res;
+}
diff --git a/src/ast_path.hpp b/src/ast_path.hpp
index e7df783..2da836f 100644
--- a/src/ast_path.hpp
+++ b/src/ast_path.hpp
@@ -154,6 +154,9 @@
 schemaPath_ dataPathToSchemaPath(const dataPath_& path);
 std::string escapeListKeyString(const std::string& what);
 
+// @brief Combining the current working directory with a new path, this function returns the resulting absolute path
+dataPath_ realPath(const dataPath_& cwd, const dataPath_& newPath);
+
 BOOST_FUSION_ADAPT_STRUCT(container_, m_name)
 BOOST_FUSION_ADAPT_STRUCT(listElement_, m_name, m_keys)
 BOOST_FUSION_ADAPT_STRUCT(leafListElement_, m_name, m_value)
diff --git a/src/interpreter.cpp b/src/interpreter.cpp
index d139e8b..73af6fc 100644
--- a/src/interpreter.cpp
+++ b/src/interpreter.cpp
@@ -57,6 +57,16 @@
     return boost::apply_visitor(pathToStringVisitor(), path);
 }
 
+void Interpreter::checkRpcPath(const dataPath_& commandPath) const
+{
+    if (auto inputPath = m_datastore.inputDatastorePath()) {
+        dataPath_ newPath = realPath(m_parser.currentPath(), commandPath);
+        if (!pathToDataString(newPath, Prefixes::WhenNeeded).starts_with(*inputPath)) {
+            throw std::runtime_error("Can't execute `cd` outside of the `prepare` context.");
+        }
+    }
+}
+
 void Interpreter::operator()(const commit_&) const
 {
     m_datastore.commitChanges();
@@ -98,9 +108,7 @@
 
 void Interpreter::operator()(const cd_& cd) const
 {
-    if (auto rpcInputPath = m_datastore.inputDatastorePath(); rpcInputPath && !pathToDataString(cd.m_path, Prefixes::WhenNeeded).starts_with(m_parser.currentNode())) {
-        throw std::runtime_error("Can't cd out of `prepare` context");
-    }
+    checkRpcPath(cd.m_path);
     m_parser.changeNode(cd.m_path);
 }
 
diff --git a/src/interpreter.hpp b/src/interpreter.hpp
index f2cfd11..3723432 100644
--- a/src/interpreter.hpp
+++ b/src/interpreter.hpp
@@ -36,6 +36,8 @@
     void operator()(const quit_&) const;
 
 private:
+    void checkRpcPath(const dataPath_& commandPath) const;
+
     [[nodiscard]] std::string buildTypeInfo(const std::string& path) const;
 
     template <typename PathType>
diff --git a/src/parser.cpp b/src/parser.cpp
index 982be11..1547478 100644
--- a/src/parser.cpp
+++ b/src/parser.cpp
@@ -85,13 +85,7 @@
 
 void Parser::changeNode(const dataPath_& name)
 {
-    if (name.m_scope == Scope::Absolute) {
-        m_curDir = name;
-    } else {
-        for (const auto& it : name.m_nodes) {
-            m_curDir.pushFragment(it);
-        }
-    }
+    m_curDir = realPath(m_curDir, name);
 }
 
 std::string Parser::currentNode() const
diff --git a/tests/interpreter.cpp b/tests/interpreter.cpp
index 0c03d89..087a9b7 100644
--- a/tests/interpreter.cpp
+++ b/tests/interpreter.cpp
@@ -456,6 +456,9 @@
         {
             REQUIRE_THROWS(boost::apply_visitor(Interpreter(parser, proxyDatastore), command_{cd_{{}, dataPath_{Scope::Absolute, {dataNode_{module_{{"example"}}, container_{"somewhereElse"}}}}}}));
             REQUIRE_THROWS(boost::apply_visitor(Interpreter(parser, proxyDatastore), command_{cd_{{}, dataPath_{Scope::Relative, {dataNode_{nodeup_{}}}}}}));
+
+            // Test that the parser allows to change the current node within the rpc context
+            REQUIRE_NOTHROW(boost::apply_visitor(Interpreter(parser, proxyDatastore), command_{cd_{{}, dataPath_{Scope::Relative, {dataNode_{container_{"payload"}}}}}}));
             boost::apply_visitor(Interpreter(parser, proxyDatastore), command_{cancel_{}});
         }