Allow any character in formspec strings with escape char
authorkwolekr <kwolekr@minetest.net>
Sun, 24 Feb 2013 21:00:35 +0000 (16:00 -0500)
committerkwolekr <kwolekr@minetest.net>
Wed, 6 Mar 2013 04:25:02 +0000 (23:25 -0500)
builtin/misc.lua
doc/lua_api.txt
src/guiFormSpecMenu.cpp
src/strfnd.h
src/util/string.h

index e018aff85ce2517194fc6d028737771efbf7fb09..496435b33b41999a2833ef49b9968faa9c56e859 100644 (file)
@@ -99,3 +99,10 @@ function minetest.setting_get_pos(name)
        return minetest.string_to_pos(value)
 end
 
+function minetest.formspec_escape(str)
+       str = string.gsub(str, "\\", "\\\\")
+       str = string.gsub(str, "%[", "\\[")
+       str = string.gsub(str, "%]", "\\]")
+       return str
+end
+
index 005d7c010af462ac90a9dae5dd5af8a0e53f5ed7..8246377e29c01e394211c69c3028b8183cda3cf7 100644 (file)
@@ -778,6 +778,9 @@ string:trim()
 minetest.pos_to_string({x=X,y=Y,z=Z}) -> "(X,Y,Z)"
 ^ Convert position to a printable string
 minetest.string_to_pos(string) -> position
+^ Same but in reverse
+minetest.formspec_escape(string) -> string
+^ escapes characters like [, ], and \ that can not be used in formspecs
 
 minetest namespace reference
 -----------------------------
index 120d6629aed2f7931f8b11992402584f93bc54e6..1754422d08637eeee0f2e1126030a1b2d6742e98 100644 (file)
@@ -207,18 +207,18 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
        Strfnd f(m_formspec_string);
        while(f.atend() == false)
        {
-               std::string type = trim(f.next("["));
+               std::string type = trim(f.next_esc("["));
                if(type == "invsize" || type == "size")
                {
                        v2f invsize;
-                       invsize.X = stof(f.next(","));
+                       invsize.X = stof(f.next_esc(","));
                        if(type == "size")
                        {
-                               invsize.Y = stof(f.next("]"));
+                               invsize.Y = stof(f.next_esc("]"));
                        }
                        else{
-                               invsize.Y = stof(f.next(";"));
-                               f.next("]");
+                               invsize.Y = stof(f.next_esc(";"));
+                               f.next_esc("]");
                        }
                        infostream<<"Form size ("<<invsize.X<<","<<invsize.Y<<")"<<std::endl;
 
@@ -242,24 +242,24 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
                }
                else if(type == "list")
                {
-                       std::string name = f.next(";");
+                       std::string name = f.next_esc(";");
                        InventoryLocation loc;
                        if(name == "context" || name == "current_name")
                                loc = m_current_inventory_location;
                        else
                                loc.deSerialize(name);
-                       std::string listname = f.next(";");
+                       std::string listname = f.next_esc(";");
                        v2s32 pos = basepos;
-                       pos.X += stof(f.next(",")) * (float)spacing.X;
-                       pos.Y += stof(f.next(";")) * (float)spacing.Y;
+                       pos.X += stof(f.next_esc(",")) * (float)spacing.X;
+                       pos.Y += stof(f.next_esc(";")) * (float)spacing.Y;
                        v2s32 geom;
-                       geom.X = stoi(f.next(","));
-                       geom.Y = stoi(f.next(";"));
+                       geom.X = stoi(f.next_esc(","));
+                       geom.Y = stoi(f.next_esc(";"));
                        infostream<<"list inv="<<name<<", listname="<<listname
                                        <<", pos=("<<pos.X<<","<<pos.Y<<")"
                                        <<", geom=("<<geom.X<<","<<geom.Y<<")"
                                        <<std::endl;
-                       std::string start_i_s = f.next("]");
+                       std::string start_i_s = f.next_esc("]");
                        s32 start_i = 0;
                        if(start_i_s != "")
                                start_i = stoi(start_i_s);
@@ -270,12 +270,12 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
                else if(type == "image")
                {
                        v2s32 pos = basepos;
-                       pos.X += stof(f.next(",")) * (float)spacing.X;
-                       pos.Y += stof(f.next(";")) * (float)spacing.Y;
+                       pos.X += stof(f.next_esc(",")) * (float)spacing.X;
+                       pos.Y += stof(f.next_esc(";")) * (float)spacing.Y;
                        v2s32 geom;
-                       geom.X = stof(f.next(",")) * (float)imgsize.X;
-                       geom.Y = stof(f.next(";")) * (float)imgsize.Y;
-                       std::string name = f.next("]");
+                       geom.X = stof(f.next_esc(",")) * (float)imgsize.X;
+                       geom.Y = stof(f.next_esc(";")) * (float)imgsize.Y;
+                       std::string name = f.next_esc("]");
                        infostream<<"image name="<<name
                                        <<", pos=("<<pos.X<<","<<pos.Y<<")"
                                        <<", geom=("<<geom.X<<","<<geom.Y<<")"
@@ -287,12 +287,12 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
                else if(type == "item_image")
                {
                        v2s32 pos = basepos;
-                       pos.X += stof(f.next(",")) * (float)spacing.X;
-                       pos.Y += stof(f.next(";")) * (float)spacing.Y;
+                       pos.X += stof(f.next_esc(",")) * (float)spacing.X;
+                       pos.Y += stof(f.next_esc(";")) * (float)spacing.Y;
                        v2s32 geom;
-                       geom.X = stof(f.next(",")) * (float)imgsize.X;
-                       geom.Y = stof(f.next(";")) * (float)imgsize.Y;
-                       std::string name = f.next("]");
+                       geom.X = stof(f.next_esc(",")) * (float)imgsize.X;
+                       geom.Y = stof(f.next_esc(";")) * (float)imgsize.Y;
+                       std::string name = f.next_esc("]");
                        infostream<<"item name="<<name
                                        <<", pos=("<<pos.X<<","<<pos.Y<<")"
                                        <<", geom=("<<geom.X<<","<<geom.Y<<")"
@@ -304,12 +304,12 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
                else if(type == "background")
                {
                        v2s32 pos = basepos;
-                       pos.X += stof(f.next(",")) * (float)spacing.X - ((float)spacing.X-(float)imgsize.X)/2;
-                       pos.Y += stof(f.next(";")) * (float)spacing.Y - ((float)spacing.Y-(float)imgsize.Y)/2;
+                       pos.X += stof(f.next_esc(",")) * (float)spacing.X - ((float)spacing.X-(float)imgsize.X)/2;
+                       pos.Y += stof(f.next_esc(";")) * (float)spacing.Y - ((float)spacing.Y-(float)imgsize.Y)/2;
                        v2s32 geom;
-                       geom.X = stof(f.next(",")) * (float)spacing.X;
-                       geom.Y = stof(f.next(";")) * (float)spacing.Y;
-                       std::string name = f.next("]");
+                       geom.X = stof(f.next_esc(",")) * (float)spacing.X;
+                       geom.Y = stof(f.next_esc(";")) * (float)spacing.Y;
+                       std::string name = f.next_esc("]");
                        infostream<<"image name="<<name
                                        <<", pos=("<<pos.X<<","<<pos.Y<<")"
                                        <<", geom=("<<geom.X<<","<<geom.Y<<")"
@@ -320,8 +320,8 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
                }
                else if(type == "field" || type == "textarea")
                {
-                       std::string fname = f.next(";");
-                       std::string flabel = f.next(";");
+                       std::string fname = f.next_esc(";");
+                       std::string flabel = f.next_esc(";");
 
                        if(fname.find(",") == std::string::npos && flabel.find(",") == std::string::npos)
                        {
@@ -372,14 +372,14 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
 
 
                                
-                               fname = f.next(";");
-                               flabel = f.next(";");
+                               fname = f.next_esc(";");
+                               flabel = f.next_esc(";");
                                if(bp_set != 2)
                                        errorstream<<"WARNING: invalid use of positioned "<<type<<" without a size[] element"<<std::endl;
                                
                        }
 
-                       std::string odefault = f.next("]");
+                       std::string odefault = f.next_esc("]");
                        std::string fdefault;
 
                        // fdefault may contain a variable reference, which
@@ -389,6 +389,9 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
                        else
                                fdefault = odefault;
 
+                       fdefault = unescape_string(fdefault);
+                       flabel = unescape_string(flabel);
+
                        FieldSpec spec = FieldSpec(
                                narrow_to_wide(fname.c_str()),
                                narrow_to_wide(flabel.c_str()),
@@ -434,15 +437,17 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
                else if(type == "label")
                {
                        v2s32 pos = padding;
-                       pos.X += stof(f.next(",")) * (float)spacing.X;
-                       pos.Y += stof(f.next(";")) * (float)spacing.Y;
+                       pos.X += stof(f.next_esc(",")) * (float)spacing.X;
+                       pos.Y += stof(f.next_esc(";")) * (float)spacing.Y;
 
                        rect = core::rect<s32>(pos.X, pos.Y+((imgsize.Y/2)-15), pos.X+300, pos.Y+((imgsize.Y/2)+15));
                        
-                       std::string flabel = f.next("]");
+                       std::string flabel = f.next_esc("]");
                        if(bp_set != 2)
                                errorstream<<"WARNING: invalid use of label without a size[] element"<<std::endl;
 
+                       flabel = unescape_string(flabel);
+
                        FieldSpec spec = FieldSpec(
                                narrow_to_wide(""),
                                narrow_to_wide(flabel.c_str()),
@@ -455,19 +460,21 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
                else if(type == "button" || type == "button_exit")
                {
                        v2s32 pos = padding;
-                       pos.X += stof(f.next(",")) * (float)spacing.X;
-                       pos.Y += stof(f.next(";")) * (float)spacing.Y;
+                       pos.X += stof(f.next_esc(",")) * (float)spacing.X;
+                       pos.Y += stof(f.next_esc(";")) * (float)spacing.Y;
                        v2s32 geom;
-                       geom.X = (stof(f.next(",")) * (float)spacing.X)-(spacing.X-imgsize.X);
-                       pos.Y += (stof(f.next(";")) * (float)imgsize.Y)/2;
+                       geom.X = (stof(f.next_esc(",")) * (float)spacing.X)-(spacing.X-imgsize.X);
+                       pos.Y += (stof(f.next_esc(";")) * (float)imgsize.Y)/2;
 
                        rect = core::rect<s32>(pos.X, pos.Y-15, pos.X+geom.X, pos.Y+15);
                        
-                       std::string fname = f.next(";");
-                       std::string flabel = f.next("]");
+                       std::string fname = f.next_esc(";");
+                       std::string flabel = f.next_esc("]");
                        if(bp_set != 2)
                                errorstream<<"WARNING: invalid use of button without a size[] element"<<std::endl;
 
+                       flabel = unescape_string(flabel);
+
                        FieldSpec spec = FieldSpec(
                                narrow_to_wide(fname.c_str()),
                                narrow_to_wide(flabel.c_str()),
@@ -483,20 +490,22 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
                else if(type == "image_button" || type == "image_button_exit")
                {
                        v2s32 pos = padding;
-                       pos.X += stof(f.next(",")) * (float)spacing.X;
-                       pos.Y += stof(f.next(";")) * (float)spacing.Y;
+                       pos.X += stof(f.next_esc(",")) * (float)spacing.X;
+                       pos.Y += stof(f.next_esc(";")) * (float)spacing.Y;
                        v2s32 geom;
-                       geom.X = (stof(f.next(",")) * (float)spacing.X)-(spacing.X-imgsize.X);
-                       geom.Y = (stof(f.next(";")) * (float)spacing.Y)-(spacing.Y-imgsize.Y);
+                       geom.X = (stof(f.next_esc(",")) * (float)spacing.X)-(spacing.X-imgsize.X);
+                       geom.Y = (stof(f.next_esc(";")) * (float)spacing.Y)-(spacing.Y-imgsize.Y);
 
                        rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y);
                        
-                       std::string fimage = f.next(";");
-                       std::string fname = f.next(";");
-                       std::string flabel = f.next("]");
+                       std::string fimage = f.next_esc(";");
+                       std::string fname = f.next_esc(";");
+                       std::string flabel = f.next_esc("]");
                        if(bp_set != 2)
                                errorstream<<"WARNING: invalid use of image_button without a size[] element"<<std::endl;
 
+                       flabel = unescape_string(flabel);
+
                        FieldSpec spec = FieldSpec(
                                narrow_to_wide(fname.c_str()),
                                narrow_to_wide(flabel.c_str()),
@@ -519,15 +528,15 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
                else if(type == "item_image_button")
                {
                        v2s32 pos = padding;
-                       pos.X += stof(f.next(",")) * (float)spacing.X;
-                       pos.Y += stof(f.next(";")) * (float)spacing.Y;
+                       pos.X += stof(f.next_esc(",")) * (float)spacing.X;
+                       pos.Y += stof(f.next_esc(";")) * (float)spacing.Y;
                        v2s32 geom;
-                       geom.X = (stof(f.next(",")) * (float)spacing.X)-(spacing.X-imgsize.X);
-                       geom.Y = (stof(f.next(";")) * (float)spacing.Y)-(spacing.Y-imgsize.Y);
+                       geom.X = (stof(f.next_esc(",")) * (float)spacing.X)-(spacing.X-imgsize.X);
+                       geom.Y = (stof(f.next_esc(";")) * (float)spacing.Y)-(spacing.Y-imgsize.Y);
                        rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y);               
-                       std::string fimage = f.next(";");
-                       std::string fname = f.next(";");
-                       std::string flabel = f.next("]");
+                       std::string fimage = f.next_esc(";");
+                       std::string fname = f.next_esc(";");
+                       std::string flabel = f.next_esc("]");
                        if(bp_set != 2)
                                errorstream<<"WARNING: invalid use of item_image_button without a size[] element"<<std::endl;           
                        IItemDefManager *idef = m_gamedef->idef();
@@ -535,6 +544,7 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
                        item.deSerialize(fimage, idef);
                        video::ITexture *texture = idef->getInventoryTexture(item.getDefinition(idef).name, m_gamedef);
                        std::string tooltip = item.getDefinition(idef).description;
+                       flabel = unescape_string(flabel);
                        FieldSpec spec = FieldSpec(
                                narrow_to_wide(fname.c_str()),
                                narrow_to_wide(flabel.c_str()),
@@ -556,7 +566,7 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
                else
                {
                        // Ignore others
-                       std::string ts = f.next("]");
+                       std::string ts = f.next_esc("]");
                        infostream<<"Unknown DrawSpec: type="<<type<<", data=\""<<ts<<"\""
                                        <<std::endl;
                }
index d28aa73b24ab7cb4253b0f0e3288c91c2fab018c..4a72edf3c50f474aa38e841ba15973a29c2772ff 100644 (file)
@@ -65,6 +65,25 @@ public:
                //std::cout<<"palautus=\""<<palautus<<"\""<<std::endl;
         return palautus;
     }
+    
+    // Returns substr of tek up to the next occurence of plop that isn't escaped with '\'
+    std::string next_esc(std::string plop) {
+               size_t n, realp;
+               
+       if (p >= tek.size())
+               return "";
+               
+               realp = p;
+               do {
+                       n = tek.find(plop, p);
+                       if (n == std::string::npos || plop == "")
+                               n = tek.length();
+                       p = n + plop.length();
+               } while (n > 0 && tek[n - 1] == '\\');
+               
+               return tek.substr(realp, n - realp);
+    }
+    
        void skip_over(std::string chars){
                while(p < tek.size()){
                        bool is = false;
@@ -128,6 +147,24 @@ public:
                //std::cout<<"palautus=\""<<palautus<<"\""<<std::endl;
         return palautus;
     }
+    
+    std::wstring next_esc(std::wstring plop) {
+               size_t n, realp;
+               
+       if (p >= tek.size())
+               return L"";
+               
+               realp = p;
+               do {
+                       n = tek.find(plop, p);
+                       if (n == std::wstring::npos || plop == L"")
+                               n = tek.length();
+                       p = n + plop.length();
+               } while (n > 0 && tek[n - 1] == '\\');
+               
+               return tek.substr(realp, n - realp);
+    }
+    
     bool atend(){
         if(p>=tek.size()) return true;
         return false;
index 2f0264bd45db471e372ff507fed5d684091f1a6a..6c48adeb3d70b0368f4f62b5a7a13d4ebcd03081 100644 (file)
@@ -286,6 +286,22 @@ inline std::string wrap_rows(const std::string &from, u32 rowlen)
        return to;
 }
 
+/*
+       Removes all \\ from a string that had been escaped (FormSpec strings)
+*/
+inline std::string unescape_string(std::string &s)
+{
+       std::string res;
+       
+       for (size_t i = 0; i <= s.length(); i++) {
+               if (s[i] == '\\')
+                       i++;
+               res += s[i];
+       }
+       
+       return res;
+}
+
 std::string translatePassword(std::string playername, std::wstring password);
 size_t curl_write_data(char *ptr, size_t size, size_t nmemb, void *userdata);
 u32 readFlagString(std::string str, FlagDesc *flagdesc);