diff --git a/include/znc/ZNCString.h b/include/znc/ZNCString.h index ce3b1f39..10262e2f 100644 --- a/include/znc/ZNCString.h +++ b/include/znc/ZNCString.h @@ -27,6 +27,7 @@ using std::pair; #define _SQL(s) CString("'" + CString(s).Escape_n(CString::ESQL) + "'") #define _URL(s) CString(s).Escape_n(CString::EURL) #define _HTML(s) CString(s).Escape_n(CString::EHTML) +#define _NAMEDFMT(s) CString(s).Escape_n(CString::ENAMEDFMT) class CString; class MCString; @@ -68,7 +69,8 @@ public: EASCII, EURL, EHTML, - ESQL + ESQL, + ENAMEDFMT } EEscape; explicit CString(bool b) : string(b ? "true" : "false") {} @@ -282,6 +284,16 @@ public: const CString& sLeft = "", const CString& sRight = "", bool bTrimQuotes = true, bool bTrimWhiteSpace = false) const; + /** Build a string from a format string, replacing values from a map. + * The format specification can contain simple named parameters that match + * keys in the given map. For example in the string "a {b} c", the key "b" + * is looked up in the map, and inserted for "{b}". + * @param sFormat The format specification. + * @param msValues A map of named parameters to their values. + * @return The string with named parameters replaced. + */ + static CString NamedFormat(const CString& sFormat, const MCString& msValues); + /** Produces a random string. * @param uLength The length of the resulting string. * @return A random string. diff --git a/src/ZNCString.cpp b/src/ZNCString.cpp index c81ae180..e5bd0ffc 100644 --- a/src/ZNCString.cpp +++ b/src/ZNCString.cpp @@ -171,6 +171,8 @@ CString::EEscape CString::ToEscape(const CString& sEsc) { return EURL; } else if (sEsc.Equals("SQL")) { return ESQL; + } else if (sEsc.Equals("NAMEDFMT")) { + return ENAMEDFMT; } return EASCII; @@ -276,6 +278,16 @@ CString CString::Escape_n(EEscape eFrom, EEscape eTo) const { } } + break; + case ENAMEDFMT: + if (*p != '\\' || iLength < (a +1)) { + ch = *p; + } else { + a++; + p++; + ch = *p; + } + break; } @@ -316,6 +328,13 @@ CString CString::Escape_n(EEscape eFrom, EEscape eTo) const { } else if (ch == '\\') { sRet += '\\'; sRet += '\\'; } else { sRet += ch; } + break; + case ENAMEDFMT: + if (ch == '\\') { sRet += '\\'; sRet += '\\'; + } else if (ch == '{') { sRet += '\\'; sRet += '{'; + } else if (ch == '}') { sRet += '\\'; sRet += '}'; + } else { sRet += ch; } + break; } } @@ -639,6 +658,51 @@ unsigned int CString::Split(const CString& sDelim, SCString& ssRet, bool bAllowE return ssRet.size(); } +CString CString::NamedFormat(const CString& sFormat, const MCString& msValues) { + CString sRet; + + CString sKey; + bool bEscape = false; + bool bParam = false; + const char* p = sFormat.c_str(); + + while (*p) { + if (!bParam) { + if (bEscape) { + sRet += *p; + bEscape = false; + } else if (*p == '\\') { + bEscape = true; + } else if (*p == '{') { + bParam = true; + sKey.clear(); + } else { + sRet += *p; + } + + } else { + if (bEscape) { + sKey += *p; + bEscape = false; + } else if (*p == '\\') { + bEscape = true; + } else if (*p == '}') { + bParam = false; + MCString::const_iterator it = msValues.find(sKey); + if (it != msValues.end()) { + sRet += (*it).second; + } + } else { + sKey += *p; + } + } + + p++; + } + + return sRet; +} + CString CString::RandomString(unsigned int uLength) { const char chars[] = "abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ"