diff --git a/src/libs/utils/stringutils.cpp b/src/libs/utils/stringutils.cpp index 5024bd5ab57..688b9d59670 100644 --- a/src/libs/utils/stringutils.cpp +++ b/src/libs/utils/stringutils.cpp @@ -115,6 +115,40 @@ QTCREATOR_UTILS_EXPORT QString withTildeHomePath(const QString &path) return outPath; } +bool AbstractMacroExpander::expandNestedMacros(const QString &str, int *pos, QString *ret) +{ + QString varName; + QChar prev; + QChar c; + + int i = *pos; + int strLen = str.length(); + varName.reserve(strLen - i); + for (; i < strLen; prev = c) { + c = str.at(i++); + if (c == QLatin1Char('}')) { + if (varName.isEmpty()) { // replace "%{}" with "%" + *ret = QString(QLatin1Char('%')); + *pos = i; + return true; + } + if (resolveMacro(varName, ret)) { + *pos = i; + return true; + } + return false; + } else if (c == QLatin1Char('{') && prev == QLatin1Char('%')) { + if (!expandNestedMacros(str, &i, ret)) + return false; + varName.chop(1); + varName += ret; + } else { + varName += c; + } + } + return false; +} + int AbstractMacroExpander::findMacro(const QString &str, int *pos, QString *ret) { forever { @@ -122,22 +156,13 @@ int AbstractMacroExpander::findMacro(const QString &str, int *pos, QString *ret) if (openPos < 0) return 0; int varPos = openPos + 2; - int closePos = str.indexOf(QLatin1Char('}'), varPos); - if (closePos < 0) - return 0; - int varLen = closePos - varPos; - if (varLen == 0) { // replace "%{}" with "%" + if (expandNestedMacros(str, &varPos, ret)) { *pos = openPos; - *ret = QString(QLatin1Char('%')); - return 3; - } - if (resolveMacro(str.mid(varPos, varLen), ret)) { - *pos = openPos; - return varLen + 3; + return varPos - openPos; } // An actual expansion may be nested into a "false" one, // so we continue right after the last %{. - *pos = varPos; + *pos = openPos + 2; } } diff --git a/src/libs/utils/stringutils.h b/src/libs/utils/stringutils.h index c4643634922..97d47d64e4d 100644 --- a/src/libs/utils/stringutils.h +++ b/src/libs/utils/stringutils.h @@ -71,6 +71,8 @@ public: //! \param ret Replacement string on output //! \return True if the expando was found virtual bool resolveMacro(const QString &name, QString *ret) = 0; +private: + bool expandNestedMacros(const QString &str, int *pos, QString *ret); }; QTCREATOR_UTILS_EXPORT void expandMacros(QString *str, AbstractMacroExpander *mx); diff --git a/tests/auto/utils_stringutils/tst_stringutils.cpp b/tests/auto/utils_stringutils/tst_stringutils.cpp index 289432cd287..ef69990737d 100644 --- a/tests/auto/utils_stringutils/tst_stringutils.cpp +++ b/tests/auto/utils_stringutils/tst_stringutils.cpp @@ -38,12 +38,20 @@ class TestMacroExpander : public Utils::AbstractMacroExpander public: virtual bool resolveMacro(const QString &name, QString *ret) { + if (name == QLatin1String("foo")) { + *ret = QLatin1String("a"); + return true; + } if (name == QLatin1String("a")) { *ret = QLatin1String("hi"); return true; } - if (name == QLatin1String("foo")) { - *ret = QLatin1String("a"); + if (name == QLatin1String("hi")) { + *ret = QLatin1String("ho"); + return true; + } + if (name == QLatin1String("hihi")) { + *ret = QLatin1String("bar"); return true; } return false; @@ -116,7 +124,15 @@ void tst_StringUtils::testMacroExpander_data() { "%{a}text%{a}", "hitexthi" }, { "%{foo}%{a}text%{a}", "ahitexthi" }, { "%{}{a}", "%{a}" }, - { "%{abc", "%{abc" } + { "%{}", "%" }, + { "test%{}", "test%" }, + { "%{}test", "%test" }, + { "%{abc", "%{abc" }, + { "%{%{a}", "%{hi" }, + { "%{%{a}}", "ho" }, + { "%{%{a}}}post", "ho}post" }, + { "%{hi%{a}}", "bar" }, + { "%{hi%{%{foo}}}", "bar" }, }; for (unsigned i = 0; i < sizeof(vals)/sizeof(vals[0]); i++)