From f17c43948b2479cbe21f742d13f714af73d820d1 Mon Sep 17 00:00:00 2001
From: mattijs <mattijs>
Date: Fri, 12 Oct 2012 17:37:38 +0100
Subject: [PATCH] ENH: #include: allow recursive substitution

---
 .../includeEntry/includeEntry.C               |   6 +-
 .../primitives/strings/stringOps/stringOps.C  | 212 ++++++++++++++++++
 .../primitives/strings/stringOps/stringOps.H  |  45 +++-
 3 files changed, 258 insertions(+), 5 deletions(-)

diff --git a/src/OpenFOAM/db/dictionary/functionEntries/includeEntry/includeEntry.C b/src/OpenFOAM/db/dictionary/functionEntries/includeEntry/includeEntry.C
index bce5e22aa22..ee191e4eeb3 100644
--- a/src/OpenFOAM/db/dictionary/functionEntries/includeEntry/includeEntry.C
+++ b/src/OpenFOAM/db/dictionary/functionEntries/includeEntry/includeEntry.C
@@ -74,10 +74,8 @@ Foam::fileName Foam::functionEntries::includeEntry::includeFileName
 )
 {
     fileName fName(is);
-    // Substitute dictionary entries
-    stringOps::inplaceExpand(fName, dict);
-    // Substitute remaining environment variables
-    fName.expand();
+    // Substitute dictionary and environment variables
+    stringOps::inplaceExpand(fName, dict, true, true);
 
     if (fName.empty() || fName.isAbsolute())
     {
diff --git a/src/OpenFOAM/primitives/strings/stringOps/stringOps.C b/src/OpenFOAM/primitives/strings/stringOps/stringOps.C
index 194309cd3c3..f44ffe3a6b7 100644
--- a/src/OpenFOAM/primitives/strings/stringOps/stringOps.C
+++ b/src/OpenFOAM/primitives/strings/stringOps/stringOps.C
@@ -248,6 +248,218 @@ Foam::string Foam::stringOps::expand
 }
 
 
+Foam::string Foam::stringOps::getVariable
+(
+    const word& name,
+    const dictionary& dict,
+    const bool allowEnvVars,
+    const bool allowEmpty
+)
+{
+    string value;
+
+    const entry* ePtr = dict.lookupScopedEntryPtr
+    (
+        name,
+        true,
+        false
+    );
+    if (ePtr)
+    {
+        OStringStream buf;
+        // Force floating point numbers to be printed with at least
+        // some decimal digits.
+        buf << fixed;
+        buf.precision(IOstream::defaultPrecision());
+
+        // fail for non-primitiveEntry
+        dynamicCast<const primitiveEntry>
+        (
+            *ePtr
+        ).write(buf, true);
+
+        value = buf.str();
+    }
+    else if (allowEnvVars)
+    {
+        value = getEnv(name);
+
+        if (value.empty())
+        {
+            FatalIOErrorIn
+            (
+                "stringOps::getVariable\n"
+                "(\n"
+                "    const word&,\n"
+                "    const dictionary&,\n"
+                "    const bool,\n"
+                "    const bool\n"
+                ")\n",
+                dict
+            )   << "Cannot find dictionary or environment variable "
+                << name << exit(FatalIOError);
+        }
+    }
+    else
+    {
+        FatalIOErrorIn
+        (
+            "stringOps::getVariable\n"
+            "(\n"
+            "    const word&,\n"
+            "    const dictionary&,\n"
+            "    const bool,\n"
+            "    const bool\n"
+            ")\n",
+            dict
+        )   << "Cannot find environment variable "
+            << name << exit(FatalIOError);
+    }
+
+    return value;
+}
+
+
+Foam::string Foam::stringOps::expand
+(
+    const string& s,
+    string::size_type& index,
+    const dictionary& dict,
+    const bool allowEnvVars,
+    const bool allowEmpty
+)
+{
+    string newString;
+
+    while (index < s.size())
+    {
+        if (s[index] == '$' && s[index+1] == '{')
+        {
+            // Recurse to parse variable name
+            index += 2;
+            string val = expand(s, index, dict, allowEnvVars, allowEmpty);
+            newString.append(val);
+        }
+        else if (s[index] == '}')
+        {
+            return getVariable(newString, dict, allowEnvVars, allowEmpty);
+        }
+        else
+        {
+            newString.append(string(s[index]));
+        }
+        index++;
+    }
+    return newString;
+}
+
+
+Foam::string& Foam::stringOps::inplaceExpand
+(
+    string& s,
+    const dictionary& dict,
+    const bool allowEnvVars,
+    const bool allowEmpty,
+    const char sigil
+)
+{
+    string::size_type begVar = 0;
+
+    // Expand $VAR or ${VAR}
+    // Repeat until nothing more is found
+    while
+    (
+        (begVar = s.find(sigil, begVar)) != string::npos
+     && begVar < s.size()-1
+    )
+    {
+        if (begVar == 0 || s[begVar-1] != '\\')
+        {
+            if (s[begVar+1] == '{')
+            {
+                // Recursive variable expansion mode
+                label stringStart = begVar;
+                begVar += 2;
+                string varValue
+                (
+                    expand
+                    (
+                        s,
+                        begVar,
+                        dict,
+                        allowEnvVars,
+                        allowEmpty
+                    )
+                );
+
+                s.std::string::replace
+                (
+                    stringStart,
+                    begVar - stringStart + 1,
+                    varValue
+                );
+            }
+            else
+            {
+                string::iterator iter = s.begin() + begVar + 1;
+
+                // more generous in accepting keywords than for env variables
+                string::size_type endVar = begVar;
+                while
+                (
+                    iter != s.end()
+                 &&
+                    (
+                        isalnum(*iter)
+                     || *iter == '.'
+                     || *iter == ':'
+                     || *iter == '_'
+                    )
+                )
+                {
+                    ++iter;
+                    ++endVar;
+                }
+
+                const word varName
+                (
+                    s.substr
+                    (
+                        begVar + 1,
+                        endVar - begVar
+                    ),
+                    false
+                );
+
+                string varValue
+                (
+                    getVariable
+                    (
+                        varName,
+                        dict,
+                        allowEnvVars,
+                        allowEmpty
+                    )
+                );
+
+                s.std::string::replace
+                (
+                    begVar,
+                    varName.size()+1,
+                    varValue
+                );
+                begVar += varValue.size();
+            }
+        }
+        else
+        {
+            ++begVar;
+        }
+    }
+    return s;
+}
+
+
 Foam::string& Foam::stringOps::inplaceExpand
 (
     string& s,
diff --git a/src/OpenFOAM/primitives/strings/stringOps/stringOps.H b/src/OpenFOAM/primitives/strings/stringOps/stringOps.H
index 98c279d7ba9..3a72467bc9c 100644
--- a/src/OpenFOAM/primitives/strings/stringOps/stringOps.H
+++ b/src/OpenFOAM/primitives/strings/stringOps/stringOps.H
@@ -2,7 +2,7 @@
   =========                 |
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
-    \\  /    A nd           | Copyright (C) 2011 OpenFOAM Foundation
+    \\  /    A nd           | Copyright (C) 2011-2012 OpenFOAM Foundation
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
 License
@@ -134,6 +134,49 @@ namespace stringOps
     );
 
 
+    //- Get dictionary or (optionally) environment variable
+    string getVariable
+    (
+        const word& name,
+        const dictionary& dict,
+        const bool allowEnvVars,
+        const bool allowEmpty
+    );
+
+
+    //- Recursively expands (dictionary or environment) variable
+    //  starting at index in string. Updates index.
+    string expand
+    (
+        const string& s,
+        string::size_type& index,
+        const dictionary& dict,
+        const bool allowEnvVars,
+        const bool allowEmpty
+    );
+
+
+    //- Inplace expand occurences of variables according to the dictionary
+    //  and optionally environment variables
+    //  Expansion includes:
+    //  -# variables
+    //    - "$VAR", "${VAR}"
+    //
+    //  with the "${}" syntax doing a recursive substitution.
+    //  Any unknown entries are left as-is
+    //
+    //  \note the leading sigil can be changed to avoid conflicts with other
+    //  string expansions
+    string& inplaceExpand
+    (
+        string& s,
+        const dictionary& dict,
+        const bool allowEnvVars,
+        const bool allowEmpty,
+        const char sigil = '$'
+    );
+
+
     //- Inplace expand occurences of variables according to the dictionary
     //  Expansion includes:
     //  -# variables
-- 
GitLab