Escape more strings: formspecs, item descriptions, infotexts...
authorEkdohibs <nathanael.courant@laposte.net>
Mon, 4 Apr 2016 16:31:00 +0000 (18:31 +0200)
committerCraig Robbins <kde.psych@gmail.com>
Sat, 23 Apr 2016 17:54:11 +0000 (03:54 +1000)
Also, change the escape character to the more standard \x1b
Thus, it can be used in the future for translation or colored text,
for example.

src/chat.cpp
src/game.cpp
src/guiFormSpecMenu.cpp
src/guiFormSpecMenu.h
src/hud.cpp
src/unittest/test_utilities.cpp
src/util/string.cpp
src/util/string.h

index 7a5196ed506bb307966f9aedb2f8b9af017e181d..ab945444f5f043f73225a00723e9521d1b9b0814 100644 (file)
@@ -679,8 +679,8 @@ ChatBackend::~ChatBackend()
 
 void ChatBackend::addMessage(std::wstring name, std::wstring text)
 {
-       name = removeChatEscapes(name);
-       text = removeChatEscapes(text);
+       name = unescape_enriched(name);
+       text = unescape_enriched(text);
 
        // Note: A message may consist of multiple lines, for example the MOTD.
        WStrfnd fnd(text);
index d513517b7836c10c00597c4f5db07afb1330742b..23f261cfd7629a02e2222d4fb28a8a211e540fed 100644 (file)
@@ -3731,7 +3731,7 @@ void Game::handlePointingAtNode(GameRunData *runData,
        NodeMetadata *meta = map.getNodeMetadata(nodepos);
 
        if (meta) {
-               infotext = utf8_to_wide(meta->getString("infotext"));
+               infotext = unescape_enriched(utf8_to_wide(meta->getString("infotext")));
        } else {
                MapNode n = map.getNodeNoEx(nodepos);
 
@@ -3807,13 +3807,15 @@ void Game::handlePointingAtObject(GameRunData *runData,
                const v3f &player_position,
                bool show_debug)
 {
-       infotext = utf8_to_wide(runData->selected_object->infoText());
+       infotext = unescape_enriched(
+               utf8_to_wide(runData->selected_object->infoText()));
 
        if (show_debug) {
                if (infotext != L"") {
                        infotext += L"\n";
                }
-               infotext += utf8_to_wide(runData->selected_object->debugInfoText());
+               infotext += unescape_enriched(utf8_to_wide(
+                       runData->selected_object->debugInfoText()));
        }
 
        if (input->getLeftState()) {
index 1a6ee91cd237893a4bff733719b432cbd577b834..23cff3eb79a0c03b9a13312d6c0d3d2a0b29a9ae 100644 (file)
@@ -609,8 +609,6 @@ void GUIFormSpecMenu::parseButton(parserData* data,std::string element,
                if(!data->explicit_size)
                        warningstream<<"invalid use of button without a size[] element"<<std::endl;
 
-               label = unescape_string(label);
-
                std::wstring wlabel = utf8_to_wide(label);
 
                FieldSpec spec(
@@ -733,7 +731,6 @@ void GUIFormSpecMenu::parseTable(parserData* data,std::string element)
                geom.X = stof(v_geom[0]) * (float)spacing.X;
                geom.Y = stof(v_geom[1]) * (float)spacing.Y;
 
-
                core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y);
 
                FieldSpec spec(
@@ -746,7 +743,7 @@ void GUIFormSpecMenu::parseTable(parserData* data,std::string element)
                spec.ftype = f_Table;
 
                for (unsigned int i = 0; i < items.size(); ++i) {
-                       items[i] = unescape_string(items[i]);
+                       items[i] = unescape_string(unescape_enriched(items[i]));
                }
 
                //now really show table
@@ -818,7 +815,7 @@ void GUIFormSpecMenu::parseTextList(parserData* data,std::string element)
                spec.ftype = f_Table;
 
                for (unsigned int i = 0; i < items.size(); ++i) {
-                       items[i] = unescape_string(items[i]);
+                       items[i] = unescape_string(unescape_enriched(items[i]));
                }
 
                //now really show list
@@ -889,7 +886,8 @@ void GUIFormSpecMenu::parseDropDown(parserData* data,std::string element)
                }
 
                for (unsigned int i=0; i < items.size(); i++) {
-                       e->addItem(utf8_to_wide(items[i]).c_str());
+                       e->addItem(unescape_string(unescape_enriched(
+                               utf8_to_wide(items[i]))).c_str());
                }
 
                if (str_initial_selection != "")
@@ -930,8 +928,6 @@ void GUIFormSpecMenu::parsePwdField(parserData* data,std::string element)
 
                core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y);
 
-               label = unescape_string(label);
-
                std::wstring wlabel = utf8_to_wide(label);
 
                FieldSpec spec(
@@ -995,8 +991,6 @@ void GUIFormSpecMenu::parseSimpleField(parserData* data,
        if(m_form_src)
                default_val = m_form_src->resolveText(default_val);
 
-       default_val = unescape_string(default_val);
-       label = unescape_string(label);
 
        std::wstring wlabel = utf8_to_wide(label);
 
@@ -1094,9 +1088,6 @@ void GUIFormSpecMenu::parseTextArea(parserData* data,
                default_val = m_form_src->resolveText(default_val);
 
 
-       default_val = unescape_string(default_val);
-       label = unescape_string(label);
-
        std::wstring wlabel = utf8_to_wide(label);
 
        FieldSpec spec(
@@ -1197,7 +1188,6 @@ void GUIFormSpecMenu::parseLabel(parserData* data,std::string element)
                if(!data->explicit_size)
                        warningstream<<"invalid use of label without a size[] element"<<std::endl;
 
-               text = unescape_string(text);
                std::vector<std::string> lines = split(text, '\n');
 
                for (unsigned int i = 0; i != lines.size(); i++) {
@@ -1243,7 +1233,8 @@ void GUIFormSpecMenu::parseVertLabel(parserData* data,std::string element)
                ((parts.size() > 2) && (m_formspec_version > FORMSPEC_API_VERSION)))
        {
                std::vector<std::string> v_pos = split(parts[0],',');
-               std::wstring text = utf8_to_wide(unescape_string(parts[1]));
+               std::wstring text = unescape_string(
+                       unescape_enriched(utf8_to_wide(parts[1])));
 
                MY_CHECKPOS("vertlabel",1);
 
@@ -1330,7 +1321,6 @@ void GUIFormSpecMenu::parseImageButton(parserData* data,std::string element,
 
                image_name = unescape_string(image_name);
                pressed_image_name = unescape_string(pressed_image_name);
-               label = unescape_string(label);
 
                std::wstring wlabel = utf8_to_wide(label);
 
@@ -1430,7 +1420,8 @@ void GUIFormSpecMenu::parseTabHeader(parserData* data,std::string element)
                e->setNotClipped(true);
 
                for (unsigned int i = 0; i < buttons.size(); i++) {
-                       e->addTab(utf8_to_wide(buttons[i]).c_str(), -1);
+                       e->addTab(unescape_string(unescape_enriched(
+                               utf8_to_wide(buttons[i]))).c_str(), -1);
                }
 
                if ((tab_index >= 0) &&
@@ -1489,7 +1480,6 @@ void GUIFormSpecMenu::parseItemImageButton(parserData* data,std::string element)
                                                m_default_tooltip_bgcolor,
                                                m_default_tooltip_color);
 
-               label = unescape_string(label);
                FieldSpec spec(
                        name,
                        utf8_to_wide(label),
@@ -1604,14 +1594,14 @@ void GUIFormSpecMenu::parseTooltip(parserData* data, std::string element)
        std::vector<std::string> parts = split(element,';');
        if (parts.size() == 2) {
                std::string name = parts[0];
-               m_tooltips[name] = TooltipSpec(unescape_string(parts[1]),
+               m_tooltips[name] = TooltipSpec(parts[1],
                        m_default_tooltip_bgcolor, m_default_tooltip_color);
                return;
        } else if (parts.size() == 4) {
                std::string name = parts[0];
                video::SColor tmp_color1, tmp_color2;
                if ( parseColorString(parts[2], tmp_color1, false) && parseColorString(parts[3], tmp_color2, false) ) {
-                       m_tooltips[name] = TooltipSpec(unescape_string(parts[1]),
+                       m_tooltips[name] = TooltipSpec(parts[1],
                                tmp_color1, tmp_color2);
                        return;
                }
@@ -2242,16 +2232,18 @@ void GUIFormSpecMenu::drawList(const ListDrawSpec &s, int phase,
                        }
 
                        // Draw tooltip
-                       std::string tooltip_text = "";
-                       if (hovering && !m_selected_item)
-                               tooltip_text = item.getDefinition(m_gamedef->idef()).description;
-                       if (tooltip_text != "") {
-                               std::vector<std::string> tt_rows = str_split(tooltip_text, '\n');
+                       std::wstring tooltip_text = L"";
+                       if (hovering && !m_selected_item) {
+                               tooltip_text = utf8_to_wide(item.getDefinition(m_gamedef->idef()).description);
+                               tooltip_text = unescape_enriched(tooltip_text);
+                       }
+                       if (tooltip_text != L"") {
+                               std::vector<std::wstring> tt_rows = str_split(tooltip_text, L'\n');
                                m_tooltip_element->setBackgroundColor(m_default_tooltip_bgcolor);
                                m_tooltip_element->setOverrideColor(m_default_tooltip_color);
                                m_tooltip_element->setVisible(true);
                                this->bringToFront(m_tooltip_element);
-                               m_tooltip_element->setText(utf8_to_wide(tooltip_text).c_str());
+                               m_tooltip_element->setText(tooltip_text.c_str());
                                s32 tooltip_width = m_tooltip_element->getTextWidth() + m_btn_height;
                                s32 tooltip_height = m_tooltip_element->getTextHeight() + 5;
                                v2u32 screenSize = driver->getScreenSize();
@@ -2504,7 +2496,7 @@ void GUIFormSpecMenu::drawMenu()
                u32 delta = 0;
                if (id == -1) {
                        m_old_tooltip_id = id;
-                       m_old_tooltip = "";
+                       m_old_tooltip = L"";
                } else {
                        if (id == m_old_tooltip_id) {
                                delta = porting::getDeltaMs(m_hovered_time, getTimeMs());
@@ -2517,11 +2509,11 @@ void GUIFormSpecMenu::drawMenu()
                if (id != -1 && delta >= m_tooltip_show_delay) {
                        for(std::vector<FieldSpec>::iterator iter =  m_fields.begin();
                                        iter != m_fields.end(); ++iter) {
-                               if ( (iter->fid == id) && (m_tooltips[iter->fname].tooltip != "") ){
+                               if (iter->fid == id && m_tooltips[iter->fname].tooltip != L"") {
                                        if (m_old_tooltip != m_tooltips[iter->fname].tooltip) {
                                                m_old_tooltip = m_tooltips[iter->fname].tooltip;
-                                               m_tooltip_element->setText(utf8_to_wide(m_tooltips[iter->fname].tooltip).c_str());
-                                               std::vector<std::string> tt_rows = str_split(m_tooltips[iter->fname].tooltip, '\n');
+                                               m_tooltip_element->setText(m_tooltips[iter->fname].tooltip.c_str());
+                                               std::vector<std::wstring> tt_rows = str_split(m_tooltips[iter->fname].tooltip, L'\n');
                                                s32 tooltip_width = m_tooltip_element->getTextWidth() + m_btn_height;
                                                s32 tooltip_height = m_tooltip_element->getTextHeight() * tt_rows.size() + 5;
                                                int tooltip_offset_x = m_btn_height;
@@ -2875,7 +2867,7 @@ bool GUIFormSpecMenu::preprocessEvent(const SEvent& event)
                                core::position2d<s32>(x, y));
                if (event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN) {
                        m_old_tooltip_id = -1;
-                       m_old_tooltip = "";
+                       m_old_tooltip = L"";
                }
                if (!isChild(hovered,this)) {
                        if (DoubleClickDetection(event)) {
index 005b9136955b14d33c09993b720d9e297c7fe638..8774d306f95d0b7bd787e5ed5962a4f351c19162 100644 (file)
@@ -29,6 +29,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "modalMenu.h"
 #include "guiTable.h"
 #include "network/networkprotocol.h"
+#include "util/string.h"
 
 class IGameDef;
 class InventoryManager;
@@ -191,18 +192,26 @@ class GUIFormSpecMenu : public GUIModalMenu
                bool scale;
        };
 
+       /* The responsibility of unescaping the strings has been shifted
+        * from the formspec parsing methods to the draw methods.
+        * There still are a few exceptions:
+        *  - Vertical label, because it modifies the string by inserting
+        *    '\n' between each character,
+        *  - Tab header, because it gives the string immediately to
+        *    Irrlicht and we can't unescape it later.
+        */
        struct FieldSpec
        {
                FieldSpec()
                {
                }
                FieldSpec(const std::string &name, const std::wstring &label,
-                               const std::wstring &fdeflt, int id) :
+                               const std::wstring &default_text, int id) :
                        fname(name),
-                       flabel(label),
-                       fdefault(fdeflt),
                        fid(id)
                {
+                       flabel = unescape_string(unescape_enriched(label));
+                       fdefault = unescape_string(unescape_enriched(default_text));
                        send = false;
                        ftype = f_Unknown;
                        is_exit = false;
@@ -235,12 +244,12 @@ class GUIFormSpecMenu : public GUIModalMenu
                }
                TooltipSpec(std::string a_tooltip, irr::video::SColor a_bgcolor,
                                irr::video::SColor a_color):
-                       tooltip(a_tooltip),
                        bgcolor(a_bgcolor),
                        color(a_color)
                {
+                       tooltip = unescape_string(unescape_enriched(utf8_to_wide(a_tooltip)));
                }
-               std::string tooltip;
+               std::wstring tooltip;
                irr::video::SColor bgcolor;
                irr::video::SColor color;
        };
@@ -252,18 +261,18 @@ class GUIFormSpecMenu : public GUIModalMenu
                }
                StaticTextSpec(const std::wstring &a_text,
                                const core::rect<s32> &a_rect):
-                       text(a_text),
                        rect(a_rect),
                        parent_button(NULL)
                {
+                       text = unescape_string(unescape_enriched(a_text));
                }
                StaticTextSpec(const std::wstring &a_text,
                                const core::rect<s32> &a_rect,
                                gui::IGUIButton *a_parent_button):
-                       text(a_text),
                        rect(a_rect),
                        parent_button(a_parent_button)
                {
+                       text = unescape_string(unescape_enriched(a_text));
                }
                std::wstring text;
                core::rect<s32> rect;
@@ -406,7 +415,7 @@ protected:
        u32 m_tooltip_show_delay;
        s32 m_hovered_time;
        s32 m_old_tooltip_id;
-       std::string m_old_tooltip;
+       std::wstring m_old_tooltip;
 
        bool m_rmouse_auto_place;
 
index 502865caa53649fcc58995d8f75198606207db29..19feaef7b57dfa54b347550dba83f8ffd5fdaf4e 100644 (file)
@@ -22,6 +22,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "hud.h"
 #include "settings.h"
 #include "util/numeric.h"
+#include "util/string.h"
 #include "log.h"
 #include "gamedef.h"
 #include "itemdef.h"
@@ -319,7 +320,7 @@ void Hud::drawLuaElements(const v3s16 &camera_offset)
                                                                                 (e->number >> 8)  & 0xFF,
                                                                                 (e->number >> 0)  & 0xFF);
                                core::rect<s32> size(0, 0, e->scale.X, text_height * e->scale.Y);
-                               std::wstring text = utf8_to_wide(e->text);
+                               std::wstring text = unescape_enriched(utf8_to_wide(e->text));
                                core::dimension2d<u32> textsize = font->getDimension(text.c_str());
                                v2s32 offset((e->align.X - 1.0) * (textsize.Width / 2),
                                             (e->align.Y - 1.0) * (textsize.Height / 2));
@@ -355,11 +356,11 @@ void Hud::drawLuaElements(const v3s16 &camera_offset)
                                                                                 (e->number >> 8)  & 0xFF,
                                                                                 (e->number >> 0)  & 0xFF);
                                core::rect<s32> size(0, 0, 200, 2 * text_height);
-                               std::wstring text = utf8_to_wide(e->name);
+                               std::wstring text = unescape_enriched(utf8_to_wide(e->name));
                                font->draw(text.c_str(), size + pos, color);
                                std::ostringstream os;
                                os << distance << e->text;
-                               text = utf8_to_wide(os.str());
+                               text = unescape_enriched(utf8_to_wide(os.str()));
                                pos.Y += text_height;
                                font->draw(text.c_str(), size + pos, color);
                                break; }
index 1785997deb79fbd9390e8ae1026a28f24a5ac42d..d73975b9f84f11076932388f9a7372bc3e6ea4e6 100644 (file)
@@ -45,6 +45,7 @@ public:
        void testStringAllowed();
        void testAsciiPrintableHelper();
        void testUTF8();
+       void testRemoveEscapes();
        void testWrapRows();
        void testIsNumber();
        void testIsPowerOfTwo();
@@ -71,6 +72,7 @@ void TestUtilities::runTests(IGameDef *gamedef)
        TEST(testStringAllowed);
        TEST(testAsciiPrintableHelper);
        TEST(testUTF8);
+       TEST(testRemoveEscapes);
        TEST(testWrapRows);
        TEST(testIsNumber);
        TEST(testIsPowerOfTwo);
@@ -253,6 +255,23 @@ void TestUtilities::testUTF8()
                == "the shovel dug a crumbly node!");
 }
 
+void TestUtilities::testRemoveEscapes()
+{
+       UASSERT(unescape_enriched<wchar_t>(
+               L"abc\x1bXdef") == L"abcdef");
+       UASSERT(unescape_enriched<wchar_t>(
+               L"abc\x1b(escaped)def") == L"abcdef");
+       UASSERT(unescape_enriched<wchar_t>(
+               L"abc\x1b((escaped with parenthesis\\))def") == L"abcdef");
+       UASSERT(unescape_enriched<wchar_t>(
+               L"abc\x1b(incomplete") == L"abc");
+       UASSERT(unescape_enriched<wchar_t>(
+               L"escape at the end\x1b") == L"escape at the end");
+       // Nested escapes not supported
+       UASSERT(unescape_enriched<wchar_t>(
+               L"abc\x1b(outer \x1b(inner escape)escape)def") == L"abcescape)def");
+}
+
 void TestUtilities::testWrapRows()
 {
        UASSERT(wrap_rows("12345678",4) == "1234\n5678");
index c8f528a77a06b296295a649c5f90066d29259433..2c4143c764c1d41ba2aebcdefa3dac9acce78725 100644 (file)
@@ -729,33 +729,6 @@ static bool parseNamedColorString(const std::string &value, video::SColor &color
        return true;
 }
 
-std::wstring removeChatEscapes(const std::wstring &s) {
-       std::wstring output;
-       size_t i = 0;
-       while (i < s.length()) {
-               if (s[i] == L'\v') {
-                       ++i;
-                       if (i == s.length()) continue;
-                       if (s[i] == L'(') {
-                               ++i;
-                               while (i < s.length() && s[i] != L')') {
-                                       if (s[i] == L'\\') {
-                                               ++i;
-                                       }
-                                       ++i;
-                               }
-                               ++i;
-                       } else {
-                               ++i;
-                       }
-                       continue;
-               }
-               output += s[i];
-               ++i;
-       }
-       return output;
-}
-
 void str_replace(std::string &str, char from, char to)
 {
        std::replace(str.begin(), str.end(), from, to);
index 9e59ab20ace57e39c75ed35fd9c154eac89f2644..40ef3e4d3700877df0f3d6bd9d98e0febe1e81f4 100644 (file)
@@ -386,14 +386,6 @@ inline void str_replace(std::string &str, const std::string &pattern,
        }
 }
 
-/**
- * Remove all chat escape sequences in \p s.
- *
- * @param s The string in which to remove escape sequences.
- * @return \p s, with escape sequences removed.
- */
-std::wstring removeChatEscapes(const std::wstring &s);
-
 /**
  * Replace all occurrences of the character \p from in \p str with \p to.
  *
@@ -476,7 +468,7 @@ inline std::string wrap_rows(const std::string &from,
  * Removes backslashes from an escaped string (FormSpec strings)
  */
 template <typename T>
-inline std::basic_string<T> unescape_string(std::basic_string<T> &s)
+inline std::basic_string<T> unescape_string(const std::basic_string<T> &s)
 {
        std::basic_string<T> res;
 
@@ -492,6 +484,40 @@ inline std::basic_string<T> unescape_string(std::basic_string<T> &s)
        return res;
 }
 
+/**
+ * Remove all escape sequences in \p s.
+ *
+ * @param s The string in which to remove escape sequences.
+ * @return \p s, with escape sequences removed.
+ */
+template <typename T>
+std::basic_string<T> unescape_enriched(const std::basic_string<T> &s)
+{
+       std::basic_string<T> output;
+       size_t i = 0;
+       while (i < s.length()) {
+               if (s[i] == '\x1b') {
+                       ++i;
+                       if (i == s.length()) continue;
+                       if (s[i] == '(') {
+                               ++i;
+                               while (i < s.length() && s[i] != ')') {
+                                       if (s[i] == '\\') {
+                                               ++i;
+                                       }
+                                       ++i;
+                               }
+                               ++i;
+                       } else {
+                               ++i;
+                       }
+                       continue;
+               }
+               output += s[i];
+               ++i;
+       }
+       return output;
+}
 
 /**
  * Checks that all characters in \p to_check are a decimal digits.