Skip to content
Snippets Groups Projects
stringOps.C 24.5 KiB
Newer Older
  • Learn to ignore specific revisions
  • /*---------------------------------------------------------------------------*\
      =========                 |
      \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
       \\    /   O peration     |
    
        \\  /    A nd           | Copyright (C) 2011-2016 OpenFOAM Foundation
    
         \\/     M anipulation  | Copyright (C) 2017 OpenCFD Ltd.
    
    -------------------------------------------------------------------------------
    License
        This file is part of OpenFOAM.
    
        OpenFOAM is free software: you can redistribute it and/or modify it
        under the terms of the GNU General Public License as published by
        the Free Software Foundation, either version 3 of the License, or
        (at your option) any later version.
    
        OpenFOAM 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 General Public License
        for more details.
    
        You should have received a copy of the GNU General Public License
        along with OpenFOAM.  If not, see <http://www.gnu.org/licenses/>.
    
    \*---------------------------------------------------------------------------*/
    
    #include "stringOps.H"
    
    #include "typeInfo.H"
    
    // * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * //
    
    namespace Foam
    {
    // Standard handling of "~/", "./" etc.
    static void standardExpansions(Foam::string& s)
    {
        if (s.empty())
        {
            return;
        }
    
        if (s[0] == '.')
        {
            // Expand a lone '.' and an initial './' into cwd
            if (s.size() == 1)
            {
                s = cwd();
            }
            else if (s[1] == '/')
            {
                s.std::string::replace(0, 1, cwd());
            }
        }
        else if (s[0] == '~')
        {
            // Expand initial ~
            //   ~/        => home directory
            //   ~OpenFOAM => site/user OpenFOAM configuration directory
            //   ~user     => home directory for specified user
    
            string user;
            fileName file;
    
            const auto slash = s.find('/');
            if (slash == std::string::npos)
            {
                user = s.substr(1);
            }
            else
            {
                user = s.substr(1, slash - 1);
                file = s.substr(slash + 1);
            }
    
            // NB: be a bit lazy and expand ~unknownUser as an
            // empty string rather than leaving it untouched.
            // otherwise add extra test
    
            if (user == "OpenFOAM")
            {
                s = findEtcFile(file);
            }
            else
            {
                s = home(user)/file;
            }
        }
    }
    }
    
    //! \cond fileScope
    //  Find the type/position of the ":-" or ":+" alternative values
    
    //  Returns 0, '-', '+' corresponding to not-found or ':-' or ':+'
    
    static inline int findParameterAlternative
    (
        const std::string& s,
        std::string::size_type& pos,
        std::string::size_type endPos
    )
    {
        while (pos != std::string::npos)
        {
            pos = s.find(':', pos);
            if (pos != std::string::npos)
            {
                if (pos < endPos)
                {
                    // in-range: check for '+' or '-' following the ':'
                    const int altType = s[pos+1];
                    if (altType == '+' || altType == '-')
                    {
                        return altType;
                    }
    
                    ++pos;    // unknown/unsupported - continue at next position
                }
                else
                {
                    // out-of-range: abort
                    pos = std::string::npos;
                }
            }
        }
    
        return 0;
    }
    //! \endcond
    
    
    
    // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
    
    
    std::string::size_type Foam::stringOps::count
    (
        const std::string& str,
        const char c
    )
    {
        std::string::size_type n = 0;
    
        for (auto iter = str.cbegin(); iter != str.cend(); ++iter)
        {
            if (*iter == c)
            {
                ++n;
            }
        }
    
        return n;
    }
    
    
    std::string::size_type Foam::stringOps::count(const char* str, const char c)
    {
        if (!str)
        {
            return 0;
        }
    
        std::string::size_type n = 0;
    
        for (const char *iter = str; *iter; ++iter)
        {
            if (*iter == c)
            {
                ++n;
            }
        }
    
        return n;
    }
    
    
    
    Foam::string Foam::stringOps::expand
    (
        const string& original,
        const HashTable<string, word, string::hash>& mapping,
        const char sigil
    )
    {
    
        string s(original);
        return inplaceExpand(s, mapping);
    
    }
    
    
    Foam::string& Foam::stringOps::inplaceExpand
    (
        string& s,
        const HashTable<string, word, string::hash>& mapping,
        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] != '\\')
            {
                // Find end of first occurrence
                string::size_type endVar = begVar;
                string::size_type delim = 0;
    
    
                // The type/position of the ":-" or ":+" alternative values
                int altType = 0;
    
                string::size_type altPos = string::npos;
    
    
                if (s[begVar+1] == '{')
                {
                    endVar = s.find('}', begVar);
                    delim = 1;
    
                    // check for ${parameter:-word} or ${parameter:+word}
    
                    if (endVar != string::npos)
                    {
    
                        altPos = begVar;
                        altType = findParameterAlternative(s, altPos, endVar);
    
                    string::const_iterator iter = s.cbegin() + begVar + 1;
    
                    // more generous in accepting keywords than for env variables
    
                        iter != s.cend()
    
                         || *iter == '.'
                         || *iter == ':'
                         || *iter == '_'
                        )
    
                if (endVar == string::npos)
                {
                    // likely parsed '${...' without closing '}' - abort
                    break;
                }
                else if (endVar == begVar)
                {
                    // parsed '${}' or $badChar  - skip over
                    begVar = endVar + 1;
                }
                else
    
                    const word varName
    
                        s.substr
                        (
                            begVar + 1 + delim,
    
                            (
                                (altPos == string::npos ? endVar : altPos)
                              - begVar - 2*delim
                            )
    
                    std::string altValue;
                    if (altPos != string::npos)
                    {
    
                        // had ":-" or ":+" alternative value
    
                        altValue = s.substr
                        (
                            altPos + 2,
                            endVar - altPos - 2*delim
                        );
                    }
    
    
    
                    auto fnd = mapping.cfind(varName);
    
                        if (altPos != string::npos && altType == '+')
                        {
                            // was found, use ":+" alternative
                            s.std::string::replace
                            (
                                begVar,
                                endVar - begVar + 1,
                                altValue
                            );
                            begVar += altValue.size();
                        }
                        else
                        {
                            // was found, use value
                            s.std::string::replace
                            (
                                begVar,
                                endVar - begVar + 1,
                                *fnd
                            );
                            begVar += (*fnd).size();
                        }
    
                    else if (altPos != string::npos && altType == '-')
    
                        // was not found, use ":-" alternative
    
                        s.std::string::replace
                        (
                            begVar,
                            endVar - begVar + 1,
                            altValue
                        );
                        begVar += altValue.size();
                    }
    
                        // substitute with nothing, also for ":+" alternative
                        s.std::string::erase(begVar, endVar - begVar + 1);
    
    Foam::string Foam::stringOps::expand
    
    (
        const string& original,
        const dictionary& dict,
        const char sigil
    )
    {
        string s(original);
    
        return inplaceExpand(s, dict, sigil);
    
    Foam::string Foam::stringOps::getVariable
    (
        const word& name,
        const dictionary& dict,
        const bool allowEnvVars,
        const bool allowEmpty
    )
    {
        string value;
    
    
        const entry* eptr = dict.lookupScopedEntryPtr
    
        {
            OStringStream buf;
            // Force floating point numbers to be printed with at least
            // some decimal digits.
            buf << fixed;
            buf.precision(IOstream::defaultPrecision());
    
    
            // Fails for non-primitiveEntry
            dynamicCast<const primitiveEntry>(*eptr).write(buf, true);
    
    
            value = buf.str();
        }
        else if (allowEnvVars)
        {
            value = getEnv(name);
    
    
            if (value.empty() && !name.empty())
            {
                // The type/position of the ":-" or ":+" alternative values
                string::size_type altPos = 0;
    
                // check for parameter:-word or parameter:+word
    
                const int altType =
                    findParameterAlternative(name, altPos, name.size()-1);
    
    
                if (altType)
                {
                    value = getEnv
                    (
                        // var-name
                        word(name.substr(0, altPos), false)
                    );
    
                    // ":-" or ":+" alternative value
                    if (value.empty() ? (altType == '-') : (altType == '+'))
                    {
                        // alternative
                        value = name.substr(altPos + 2);
                    }
                }
            }
    
        if (!allowEmpty && value.empty())
        {
            if (allowEnvVars)
    
                FatalIOErrorInFunction
    
                (
                    dict
                )   << "Cannot find dictionary or environment variable "
                    << name << exit(FatalIOError);
            }
    
            else
            {
                FatalIOErrorInFunction
                (
                    dict
                )   << "Cannot find dictionary 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
    
                    auto stringStart = begVar;
    
                    begVar += 2;
                    string varValue
                    (
                        expand
                        (
                            s,
                            begVar,
                            dict,
                            allowEnvVars,
                            allowEmpty
                        )
                    );
    
                    s.std::string::replace
                    (
                        stringStart,
                        begVar - stringStart + 1,
                        varValue
                    );
    
    
                    begVar = stringStart+varValue.size();
    
                    string::const_iterator iter = s.cbegin() + begVar + 1;
    
                    string::size_type endVar = begVar;
    
    
                    // more generous in accepting keywords than for env variables
                    while
                    (
    
                        iter != s.cend()
    
                         || *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;
            }
        }
    
        // Standard handling of "~/", "./" etc.
        standardExpansions(s);
    
    Foam::string& Foam::stringOps::inplaceExpand
    
    (
        string& s,
        const dictionary& dict,
        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] != '\\')
            {
                // Find end of first occurrence
                string::size_type endVar = begVar;
                string::size_type delim = 0;
    
                if (s[begVar+1] == '{')
                {
                    endVar = s.find('}', begVar);
                    delim = 1;
                }
                else
                {
    
                    string::const_iterator iter = s.cbegin() + begVar + 1;
    
    
                    // more generous in accepting keywords than for env variables
                    while
                    (
    
                        iter != s.cend()
    
                         || *iter == '.'
                         || *iter == ':'
                         || *iter == '_'
                        )
                    )
                    {
                        ++iter;
                        ++endVar;
                    }
                }
    
    
                if (endVar == string::npos)
                {
                    // likely parsed '${...' without closing '}' - abort
                    break;
                }
                else if (endVar == begVar)
                {
                    // parsed '${}' or $badChar  - skip over
                    begVar = endVar + 1;
                }
                else
    
                {
                    const word varName
                    (
                        s.substr
                        (
                            begVar + 1 + delim,
                            endVar - begVar - 2*delim
                        ),
                        false
                    );
    
    
    
                    // Lookup in the dictionary without wildcards.
                    // See note in primitiveEntry
                    const entry* eptr = dict.lookupScopedEntryPtr
    
    
                    // if defined - copy its entries
    
                    if (eptr)
    
                    {
                        OStringStream buf;
    
                        // Force floating point numbers to be printed with at least
                        // some decimal digits.
                        buf << fixed;
                        buf.precision(IOstream::defaultPrecision());
    
                        if (eptr->isDict())
    
                            eptr->dict().write(buf, false);
    
                            // Fail for non-primitiveEntry
    
                            dynamicCast<const primitiveEntry>
                            (
    
                            ).write(buf, true);
                        }
    
                        s.std::string::replace
                        (
                            begVar,
                            endVar - begVar + 1,
                            buf.str()
                        );
                        begVar += buf.str().size();
                    }
                    else
                    {
    
                        // not defined - leave original string untouched
    
    Foam::string Foam::stringOps::expand
    
        const bool allowEmpty
    
        return inplaceExpand(s, allowEmpty);
    
    Foam::string& Foam::stringOps::inplaceExpand
    
        const bool allowEmpty
    
    )
    {
        string::size_type begVar = 0;
    
        // Expand $VARS
        // Repeat until nothing more is found
        while
        (
            (begVar = s.find('$', begVar)) != string::npos
         && begVar < s.size()-1
        )
        {
            if (begVar == 0 || s[begVar-1] != '\\')
            {
                // Find end of first occurrence
                string::size_type endVar = begVar;
                string::size_type delim = 0;
    
    
                // The type/position of the ":-" or ":+" alternative values
                int altType = 0;
    
                string::size_type altPos = string::npos;
    
    
                if (s[begVar+1] == '{')
                {
                    endVar = s.find('}', begVar);
                    delim = 1;
    
                    // check for ${parameter:-word} or ${parameter:+word}
    
                    if (endVar != string::npos)
                    {
    
                        altPos = begVar;
                        altType = findParameterAlternative(s, altPos, endVar);
    
                    string::const_iterator iter = s.cbegin() + begVar + 1;
    
                        iter != s.cend()
    
                     && (std::isalnum(*iter) || *iter == '_')
    
    
                if (endVar == string::npos)
                {
                    // likely parsed '${...' without closing '}' - abort
                    break;
                }
                else if (endVar == begVar)
                {
                    // parsed '${}' or $badChar  - skip over
                    begVar = endVar + 1;
                }
                else
    
                    const word varName
    
                        s.substr
                        (
                            begVar + 1 + delim,
    
                            (
                                (altPos == string::npos ? endVar : altPos)
                              - begVar - 2*delim
                            )
    
                    std::string altValue;
                    if (altPos != string::npos)
                    {
    
                        // had ":-" or ":+" alternative value
    
                        altValue = s.substr
                        (
                            altPos + 2,
                            endVar - altPos - 2*delim
                        );
                    }
    
    
                    const string varValue = getEnv(varName);
    
                        if (altPos != string::npos && altType == '+')
                        {
                            // was found, use ":+" alternative
                            s.std::string::replace
                            (
                                begVar,
                                endVar - begVar + 1,
                                altValue
                            );
                            begVar += altValue.size();
                        }
                        else
                        {
                            // was found, use value
                            s.std::string::replace
                            (
                                begVar,
                                endVar - begVar + 1,
                                varValue
                            );
                            begVar += varValue.size();
                        }
    
                    else if (altPos != string::npos)
                    {
    
                        // use ":-" or ":+" alternative values
                        if (altType == '-')
                        {
                            // was not found, use ":-" alternative
                            s.std::string::replace
                            (
                                begVar,
                                endVar - begVar + 1,
                                altValue
                            );
                            begVar += altValue.size();
                        }
                        else
                        {
                            // was not found, ":+" alternative implies
                            // substitute with nothing
                            s.std::string::erase(begVar, endVar - begVar + 1);
                        }
    
                    else if (allowEmpty)
    
                        s.std::string::erase(begVar, endVar - begVar + 1);
    
                        FatalErrorInFunction
                            << "Unknown variable name '" << varName << "'"
    
        // Standard handling of "~/", "./" etc.
        standardExpansions(s);
    
    bool Foam::stringOps::inplaceReplaceVar(string& s, const word& varName)
    {
        if (s.empty() || varName.empty())
        {
            return false;
        }
    
        const string content(getEnv(varName));
        if (content.empty())
        {
            return false;
        }
    
        const std::string::size_type i = s.find(content);
        if (i == std::string::npos)
        {
            return false;
        }
    
    
        s.replace(i, content.size(), string("${" + varName + "}"));
        return true;
    
    Foam::string Foam::stringOps::trimLeft(const string& s)
    {
        if (!s.empty())
        {
            string::size_type beg = 0;
    
            while (beg < s.size() && std::isspace(s[beg]))
    
            {
                ++beg;
            }
    
            if (beg)
            {
                return s.substr(beg);
            }
        }
    
        return s;
    }
    
    
    Foam::string& Foam::stringOps::inplaceTrimLeft(string& s)
    {
        if (!s.empty())
        {
            string::size_type beg = 0;
    
            while (beg < s.size() && std::isspace(s[beg]))
    
            {
                ++beg;
            }
    
            if (beg)
            {
                s.erase(0, beg);
            }
        }
    
        return s;
    }
    
    
    Foam::string Foam::stringOps::trimRight(const string& s)
    {
    
        if (!s.empty())
        {
            string::size_type sz = s.size();
    
            while (sz && std::isspace(s[sz-1]))
    
        return s;
    }
    
    
    Foam::string& Foam::stringOps::inplaceTrimRight(string& s)
    {
    
        if (!s.empty())
        {
            string::size_type sz = s.size();
    
            while (sz && std::isspace(s[sz-1]))
    
        return s;
    }
    
    
    Foam::string Foam::stringOps::trim(const string& original)
    {
    
        return trimLeft(trimRight(original));
    
    }
    
    
    Foam::string& Foam::stringOps::inplaceTrim(string& s)
    {