--[[ * Ashita - Copyright (c) 2014 - 2017 atom0s [atom0s@live.com] * * This work is licensed under the Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License. * To view a copy of this license, visit http://creativecommons.org/licenses/by-nc-nd/4.0/ or send a letter to * Creative Commons, PO Box 1866, Mountain View, CA 94042, USA. * * By using Ashita, you agree to the above license and its terms. * * Attribution - You must give appropriate credit, provide a link to the license and indicate if changes were * made. You must do so in any reasonable manner, but not in any way that suggests the licensor * endorses you or your use. * * Non-Commercial - You may not use the material (Ashita) for commercial purposes. * * No-Derivatives - If you remix, transform, or build upon the material (Ashita), you may not distribute the * modified material. You are, however, allowed to submit the modified works back to the original * Ashita project in attempt to have it added to the original project. * * You may not apply legal terms or technological measures that legally restrict others * from doing anything the license permits. * * No warranties are given. ]]-- ---------------------------------------------------------------------------------------------------- -- func: string.contains -- desc: Determines if a string contains the given sub-string. ---------------------------------------------------------------------------------------------------- function string.contains(s, v) return s:find(v, nil, true) ~= nil; end ---------------------------------------------------------------------------------------------------- -- func: string.startswith -- desc: Determines if a string begins with a specific string. ---------------------------------------------------------------------------------------------------- function string.startswith(s, v) return s:sub(1, #v) == v; end ---------------------------------------------------------------------------------------------------- -- func: string.endswith -- desc: Determines if a string ends with a specific string. ---------------------------------------------------------------------------------------------------- function string.endswith(s, v) return s:sub(-#v) == v; end ---------------------------------------------------------------------------------------------------- -- func: string.upperfirst -- desc: Uppercases the first letter of a string. ---------------------------------------------------------------------------------------------------- function string.upperfirst(s) return s:sub(1, 1):upper() .. s:sub(2); end ---------------------------------------------------------------------------------------------------- -- func: string.toproper -- desc: Converts a string to proper casing. ---------------------------------------------------------------------------------------------------- function string.toproper(s) local ret = ''; local t = { }; for x = 1, s:len() do t[x] = s:sub(x, x); if (t[x - 1] == ' ' or x == 1) then t[x] = t[x]:upperfirst(); end ret = ret .. t[x]; end return ret; end ---------------------------------------------------------------------------------------------------- -- func: string.insert -- desc: Inserts data into the current string at the given position. ---------------------------------------------------------------------------------------------------- function string.insert(s, p, v) local part = s:sub(1, p - 1); return part .. v .. s:sub(#part + 1); end ---------------------------------------------------------------------------------------------------- -- func: string.remove -- desc: Removes the character at the given index. ---------------------------------------------------------------------------------------------------- function string.remove(s, index) return s:sub(0, index - 1) .. s:sub(index + 1); end ---------------------------------------------------------------------------------------------------- -- func: string.lpad -- desc: Pads a string 'n' times with the given string. ---------------------------------------------------------------------------------------------------- function string.lpad(s, v, n) return (v:rep(n) .. s):sub(-(n > #s and n or #s)); end ---------------------------------------------------------------------------------------------------- -- func: string.rpad -- desc: Pads a string 'n' times with the given string. ---------------------------------------------------------------------------------------------------- function string.rpad(s, v, n) return (s .. v:rep(n)):sub(1, -(n > #s and n or #s)); end ---------------------------------------------------------------------------------------------------- -- func: string.hex -- desc: Converts a strings value to a hex string. ---------------------------------------------------------------------------------------------------- function string.hex(s, sep) sep = sep or ' '; local ret = ''; for _, v in pairs(s:totable()) do ret = ret .. string.format('%02X', v) .. sep; end return ret:trim(); end ---------------------------------------------------------------------------------------------------- -- func: string.fromhex -- desc: Converts a hex value to a string. ---------------------------------------------------------------------------------------------------- function string.fromhex(s) s = s:gsub('%s*0x', ''):gsub('[^%w]', ''); return (s:gsub('%w%w', function(c) return string.char(tonumber(c, 16)); end)); end ---------------------------------------------------------------------------------------------------- -- func: string.totable -- desc: Converts the characters of a string to a table. ---------------------------------------------------------------------------------------------------- function string.totable(s) local ret = { }; for x = 1, string.len(s) do ret[x] = string.byte(s, x); end return ret; end ---------------------------------------------------------------------------------------------------- -- func: string.clean -- desc: Cleans a string of whitespace. ---------------------------------------------------------------------------------------------------- function string.clean(s, trimend) if (trimend == nil) then trimend = true; end if (trimend) then return s:gsub('%s+', ' '):trim(); else return (s:gsub('%s+', ' ')); end end ---------------------------------------------------------------------------------------------------- -- func: string.trimstart -- desc: Trims the start of a string for whitespace. ---------------------------------------------------------------------------------------------------- function string.trimstart(s, c) if (not c) then c = ' '; end s = string.reverse(s); s = string.trimend(s, c); return string.reverse(s); end ---------------------------------------------------------------------------------------------------- -- func: string.trimend -- desc: Trims the end of a string for whitespace. ---------------------------------------------------------------------------------------------------- function string.trimend(s, c) if (not c) then c = ' '; end if (string.sub(s, -1) == c) then s = string.sub(s, 0, -2); s = string.trimend(s, c); end return s; end ---------------------------------------------------------------------------------------------------- -- func: string.trim -- desc: Trims a string of whitespace. ---------------------------------------------------------------------------------------------------- function string.trim(s, c) if (not c) then c = ' '; end s = string.trimstart(s, c); s = string.trimend(s, c); return s; end ---------------------------------------------------------------------------------------------------- -- func: string.args -- desc: Returns a table of arguments parsed from a string. ---------------------------------------------------------------------------------------------------- function string:args() local STATE_NONE = 0; -- Currently within nothing.. local STATE_WORD = 1; -- Currently within a word.. local STATE_QUOTE = 2; -- Currently within a quote.. local currentState = STATE_NONE; local currentChar = nil; local nextChar = nil; local stringStart = nil; local args = { }; -- Loop the string and self any arguments.. for x = 1, string.len(self) do -- Read the current characters.. currentChar = string.sub(self, x, x); nextChar = string.sub(self, x + 1, x+1); -- Handle non-state.. if (currentState == STATE_NONE) then if (currentChar == '"') then stringStart = x+1; currentState = STATE_QUOTE; else if (currentChar ~= ' ') then stringStart = x; currentState = STATE_WORD; end end -- Handle quoted string state.. elseif (currentState == STATE_QUOTE) then if (currentChar == '"') then currentState = STATE_NONE; table.insert(args, #args+1, string.sub(self, stringStart, x - 1)); end -- Handle word string state.. elseif (currentState == STATE_WORD) then if (currentChar == ' ' or nextChar == nil or nextChar == '\0') then currentState = STATE_NONE; table.insert(args, #args+1, string.sub(self, stringStart, x - 1)); end else print('args - Unknown state.'); end end -- If in a word insert into the args table.. if (currentState == STATE_WORD) then table.insert(args, #args + 1, string.sub(self, stringStart, #self + 1)); end -- Return the found arguments.. return args; end ---------------------------------------------------------------------------------------------------- -- func: string.is_quoted_arg -- desc: Determines if the string is quoted. ---------------------------------------------------------------------------------------------------- function string.is_quoted_arg() local arg = string.match(self, "^\"(.*)\"$"); return (arg ~= nil), arg; end ---------------------------------------------------------------------------------------------------- -- func: string.parseargs -- desc: Returns a table of arguments parsed from a string. ---------------------------------------------------------------------------------------------------- function string:parseargs() local STATE_NONE = 0; -- Currently within nothing.. local STATE_WORD = 1; -- Currently within a word.. local STATE_QUOTE = 2; -- Currently within a quote.. local currentState = STATE_NONE; local currentChar = nil; local nextChar = nil; local stringStart = nil; local prefix = nil; local args = { }; -- Loop the string and self any arguments.. for x = 1, string.len(self) do -- Read the current characters.. currentChar = string.sub(self, x, x); nextChar = string.sub(self, x + 1, x + 1); -- Ensure the command starts with a slash.. if (x == 1 and currentChar ~= '/') then return nil; end -- Handle non-state.. if (currentState == STATE_NONE) then if (currentChar == '"') then stringStart = x; currentState = STATE_QUOTE; elseif (currentChar ~= ' ') then stringStart = x; currentState = STATE_WORD; end -- Handle quoted string state.. elseif (currentState == STATE_QUOTE) then if (currentChar == '"') then table.insert(args, #args + 1, string.sub(self, stringStart, x)); currentState = STATE_NONE; end -- Handle word string state.. elseif (currentState == STATE_WORD) then if (currentChar == ' ') then table.insert(args, #args+1, string.sub(self, stringStart, x - 1)); if (prefix == nil) then prefix = args[#args]; end currentState = STATE_NONE; elseif (nextChar == nil or nextChar == '\0') then -- This section never actually seems to get hit during processing. -- Regardless, it needs to use a different endpoint than the block above. table.insert(args, #args + 1, string.sub(self, stringStart, x)); if (prefix == nil) then prefix = args[#args]; end currentState = STATE_NONE; elseif (prefix == nil and currentChar == '/' and x == (stringStart + 1)) then -- If command line starts with //, put that in its own argument field table.insert(args, #args + 1, string.sub(self, stringStart, x)); prefix = args[#args]; currentState = STATE_NONE; elseif (currentChar == '"') then -- A quote mark should start a new quote arg, even if there is no space delimiter. table.insert(args, #args + 1, string.sub(self, stringStart, x - 1)); currentState = STATE_QUOTE; stringStart = x; end else print('parseargs - Unknown state.'); end end -- If in a word insert into the args table.. if (currentState == STATE_WORD) then table.insert(args, #args + 1, string.sub(self, stringStart, #self)); end -- Return the found arguments.. return args; end ---------------------------------------------------------------------------------------------------- -- func: string.strip_colors -- desc: Strips FFXi based colors from a string. ---------------------------------------------------------------------------------------------------- function string.strip_colors(s) return (s:gsub('[' .. string.char(0x1E, 0x1F, 0x7F) .. '].', '')); end ---------------------------------------------------------------------------------------------------- -- func: string.strip_translate -- desc: Strips FFXi based auto-translate tags from a string. ---------------------------------------------------------------------------------------------------- function string.strip_translate(s) return (s:gsub(string.char(0xEF) .. '[' .. string.char(0x27, 0x28) .. ']', '')); end