--- Auxiliary functions for string manipulation
--
-- @author Daniel Miller
-- @copyright Same as Nmap--See https://nmap.org/book/man-legal.html
-- @class module
-- @name stringaux
local assert = assert
local type = type
local string = require "string"
local byte = string.byte
local find = string.find
local match = string.match
local sub = string.sub
local gsub = string.gsub
local format = string.format
local lower = string.lower
local upper = string.upper
local table = require "table"
local concat = table.concat
local _ENV = {}
--- Join a list of strings with a separator string.
--
-- This is Lua's table.concat
function with the parameters
-- swapped for coherence.
-- @usage
-- stringaux.strjoin(", ", {"Anna", "Bob", "Charlie", "Dolores"})
-- --> "Anna, Bob, Charlie, Dolores"
-- @param delimiter String to delimit each element of the list.
-- @param list Array of strings to concatenate.
-- @return Concatenated string.
function strjoin(delimiter, list)
assert(type(delimiter) == "string" or type(delimiter) == nil, "delimiter is of the wrong type! (did you get the parameters backward?)")
return concat(list, delimiter);
end
--- Split a string at a given delimiter, which may be a pattern.
--
-- If you want to loop over the resulting values, consider using string.gmatch instead.
-- @usage
-- stringaux.strsplit(",%s*", "Anna, Bob, Charlie, Dolores")
-- --> { "Anna", "Bob", "Charlie", "Dolores" }
-- @param pattern Pattern that separates the desired strings.
-- @param text String to split.
-- @return Array of substrings without the separating pattern.
-- @see string.gmatch
function strsplit(pattern, text)
local list, pos = {}, 1;
assert(pattern ~= "", "delimiter matches empty string!");
while true do
local first, last = find(text, pattern, pos);
if first then -- found?
list[#list+1] = sub(text, pos, first-1);
pos = last+1;
else
list[#list+1] = sub(text, pos);
break;
end
end
return list;
end
-- This pattern must match the percent sign '%' since it is used in
-- escaping.
local FILESYSTEM_UNSAFE = "[^a-zA-Z0-9._-]"
local function _escape_helper (c)
return format("%%%02x", byte(c))
end
---
-- Escape a string to remove bytes and strings that may have meaning to
-- a filesystem, such as slashes.
--
-- All bytes are escaped, except for:
-- * alphabetic a
-z
and A
-Z
-- * digits 0-9
-- * .
_
-
-- In addition, the strings "."
and ".."
have
-- their characters escaped.
--
-- Bytes are escaped by a percent sign followed by the two-digit
-- hexadecimal representation of the byte value.
-- * filename_escape("filename.ext") --> "filename.ext"
-- * filename_escape("input/output") --> "input%2foutput"
-- * filename_escape(".") --> "%2e"
-- * filename_escape("..") --> "%2e%2e"
-- This escaping is somewhat like that of JavaScript
-- encodeURIComponent
, except that fewer bytes are
-- whitelisted, and it works on bytes, not Unicode characters or UTF-16
-- code points.
function filename_escape(s)
if s == "." then
return "%2e"
elseif s == ".." then
return "%2e%2e"
else
return (gsub(s, FILESYSTEM_UNSAFE, _escape_helper))
end
end
--- Returns the case insensitive pattern of given parameter
--
-- Useful while doing case insensitive pattern match using string library.
-- https://stackoverflow.com/questions/11401890/case-insensitive-lua-pattern-matching/11402486#11402486
--
-- @usage stringaux.ipattern("user")
-- --> "[uU][sS][eE][rR]"
-- @param pattern The string
-- @return A case insensitive patterned string
function ipattern(pattern)
local in_brackets = false
-- Find an optional '%' (group 2) followed by any character (group 3)
local p = gsub(pattern, "(%%?)(.)", function(percent, letter)
if percent ~= "" then
-- It's a %-escape, return as-is
return nil
elseif not match(letter, "%a") then
-- It's not alpha. Update bracket status and return as-is
if letter == "[" then
in_brackets = true
elseif letter == "]" then
in_brackets = false
end
return nil
elseif not in_brackets then
-- Else, return a case-insensitive character class of the matched letter
return format("[%s%s]", lower(letter), upper(letter))
end
end)
return p
end
return _ENV