-- worldeditadditions_core = { modpath="/home/sbrl/.minetest/worlds/Mod-Sandbox/worldmods/WorldEditAdditions/worldeditadditions_core/" } local table_map, polyfill if minetest then local wea_c = worldeditadditions_core table_map = dofile(wea_c.modpath.."/utils/table/table_map.lua") polyfill = wea_c else table_map = require("worldeditadditions_core.utils.table.table_map") polyfill = require("worldeditadditions_core.utils.strings.polyfill") end local function is_whitespace(char) return char:match("%s") end local function split_shell(text, autotrim) local text_length = #text local scan_pos = 1 local result = { } local acc = {} local mode = "NORMAL" -- NORMAL, INSIDE_QUOTES_SINGLE, INSIDE_QUOTES_DOUBLE -- print("\n\n\n\n\nDEBUG:split_shell START text", text, "autotrim", autotrim) for i=1,text_length do local prevchar = "" local curchar = text:sub(i,i) local nextchar = "" local nextnextchar = "" if i > 1 then prevchar = text:sub(i-1, i-1) end if i < text_length then nextchar = text:sub(i+1, i+1) end if i+1 < text_length then nextnextchar = text:sub(i+2, i+2) end print("mode", mode, "prevchar", prevchar, "curchar", curchar, "nextchar", nextchar) if mode == "NORMAL" then if is_whitespace(curchar) and #acc > 0 then local nextval = polyfill.trim(table.concat(acc, "")) if #nextval > 0 then table.insert(result, table.concat(acc, "")) end acc = {} elseif (curchar == "\"" or curchar == "'") and #acc == 0 and prevchar ~= "\\" then if curchar == "\"" then mode = "INSIDE_QUOTES_DOUBLE" else mode = "INSIDE_QUOTES_SINGLE" end else table.insert(acc, curchar) end elseif mode == "INSIDE_QUOTES_DOUBLE" then if curchar == "\"" and prevchar ~= "\\" and (is_whitespace(nextchar) or #nextchar == 0) then -- It's the end of a quote! mode = "NORMAL" elseif (curchar == "\\" and ( nextchar ~= "\"" or (nextchar == "\"" and not is_whitespace(nextnextchar)) )) or curchar ~= "\\" then -- It's a regular character table.insert(acc, curchar) end elseif mode == "INSIDE_QUOTES_SINGLE" then if curchar == "'" and prevchar ~= "\\" and (is_whitespace(nextchar) or nextchar == "") then -- It's the end of a quote! mode = "NORMAL" elseif (curchar == "\\" and ( nextchar ~= "'" or (nextchar == "'" and not is_whitespace(nextnextchar)) )) or curchar ~= "\\" then -- It's a regular character table.insert(acc, curchar) end end end if #acc > 0 then table.insert(result, table.concat(acc, "")) end -- Unwind all escapes by 1 level return table_map(result, function(str) return str:gsub("\\([\"'\\])", "%1") end) end return split_shell -- local function test(text) -- print("Source", text) -- for i,value in ipairs(split_shell(text)) do -- print("i", i, "→", value) -- end -- print("************") -- end -- -- test("yay yay yay") -- test("dirt \"snow block\"") -- test("yay \"yay yay\" yay") -- test("yay \"yay\\\" yay\" yay") -- test("yay \"yay 'inside quotes' yay\\\"\" yay") -- test("yay 'inside quotes' another") -- test("y\"ay \"yay 'in\\\"side quotes' yay\" y\\\"ay")