if not modules then modules = { } end modules ['l-string'] = {
    version   = 1.001,
    comment   = "companion to luat-lib.mkiv",
    author    = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
    copyright = "PRAGMA ADE / ConTeXt Development Team",
    license   = "see context related readme files"
}

local string = string
local sub, gmatch, format, char, byte, rep, lower = string.sub, string.gmatch, string.format, string.char, string.byte, string.rep, string.lower
local lpegmatch, patterns = lpeg.match, lpeg.patterns
local P, S, C, Ct, Cc, Cs = lpeg.P, lpeg.S, lpeg.C, lpeg.Ct, lpeg.Cc, lpeg.Cs

-- Some functions are already defined in l-lpeg and maybe some from here will
-- move there (unless we also expose caches).

-- if not string.split then
--
--     function string.split(str,pattern)
--         local t = { }
--         if #str > 0 then
--             local n = 1
--             for s in gmatch(str..pattern,"(.-)"..pattern) do
--                 t[n] = s
--                 n = n + 1
--             end
--         end
--         return t
--     end
--
-- end

-- function string.unquoted(str)
--     return (gsub(str,"^([\"\'])(.*)%1$","%2")) -- interesting pattern
-- end

local unquoted = patterns.squote * C(patterns.nosquote) * patterns.squote
               + patterns.dquote * C(patterns.nodquote) * patterns.dquote

function string.unquoted(str)
    return lpegmatch(unquoted,str) or str
end

-- print(string.unquoted("test"))
-- print(string.unquoted([["t\"est"]]))
-- print(string.unquoted([["t\"est"x]]))
-- print(string.unquoted("\'test\'"))
-- print(string.unquoted('"test"'))
-- print(string.unquoted('"test"'))

function string.quoted(str)
    return format("%q",str) -- always double quote
end

function string.count(str,pattern) -- variant 3
    local n = 0
    for _ in gmatch(str,pattern) do -- not for utf
        n = n + 1
    end
    return n
end

function string.limit(str,n,sentinel) -- not utf proof
    if #str > n then
        sentinel = sentinel or "..."
        return sub(str,1,(n-#sentinel)) .. sentinel
    else
        return str
    end
end

local stripper     = patterns.stripper
local fullstripper = patterns.fullstripper
local collapser    = patterns.collapser
local longtostring = patterns.longtostring

function string.strip(str)
    return lpegmatch(stripper,str) or ""
end

function string.fullstrip(str)
    return lpegmatch(fullstripper,str) or ""
end

function string.collapsespaces(str)
    return lpegmatch(collapser,str) or ""
end

function string.longtostring(str)
    return lpegmatch(longtostring,str) or ""
end

-- function string.is_empty(str)
--     return not find(str,"%S")
-- end

local pattern = P(" ")^0 * P(-1) -- maybe also newlines

-- patterns.onlyspaces = pattern

function string.is_empty(str)
    if str == "" then
        return true
    else
        return lpegmatch(pattern,str) and true or false
    end
end

-- if not string.escapedpattern then
--
--     local patterns_escapes = {
--         ["%"] = "%%",
--         ["."] = "%.",
--         ["+"] = "%+", ["-"] = "%-", ["*"] = "%*",
--         ["["] = "%[", ["]"] = "%]",
--         ["("] = "%(", [")"] = "%)",
--      -- ["{"] = "%{", ["}"] = "%}"
--      -- ["^"] = "%^", ["$"] = "%$",
--     }
--
--     local simple_escapes = {
--         ["-"] = "%-",
--         ["."] = "%.",
--         ["?"] = ".",
--         ["*"] = ".*",
--     }
--
--     function string.escapedpattern(str,simple)
--         return (gsub(str,".",simple and simple_escapes or patterns_escapes))
--     end
--
--     function string.topattern(str,lowercase,strict)
--         if str == "" then
--             return ".*"
--         else
--             str = gsub(str,".",simple_escapes)
--             if lowercase then
--                 str = lower(str)
--             end
--             if strict then
--                 return "^" .. str .. "$"
--             else
--                 return str
--             end
--         end
--     end
--
-- end

--- needs checking

local anything     = patterns.anything
local allescapes   = Cc("%") * S(".-+%?()[]*") -- also {} and ^$ ?
local someescapes  = Cc("%") * S(".-+%()[]")   -- also {} and ^$ ?
local matchescapes = Cc(".") * S("*?")         -- wildcard and single match

local pattern_a = Cs ( ( allescapes + anything )^0 )
local pattern_b = Cs ( ( someescapes + matchescapes + anything )^0 )
local pattern_c = Cs ( Cc("^") * ( someescapes + matchescapes + anything )^0 * Cc("$") )

function string.escapedpattern(str,simple)
    return lpegmatch(simple and pattern_b or pattern_a,str)
end

function string.topattern(str,lowercase,strict)
    if str=="" or type(str) ~= "string" then
        return ".*"
    elseif strict then
        str = lpegmatch(pattern_c,str)
    else
        str = lpegmatch(pattern_b,str)
    end
    if lowercase then
        return lower(str)
    else
        return str
    end
end

-- print(string.escapedpattern("12+34*.tex",false))
-- print(string.escapedpattern("12+34*.tex",true))
-- print(string.topattern     ("12+34*.tex",false,false))
-- print(string.topattern     ("12+34*.tex",false,true))

function string.valid(str,default)
    return (type(str) == "string" and str ~= "" and str) or default or nil
end

-- handy fallback

string.itself  = function(s) return s end

-- also handy (see utf variant)

local pattern = Ct(C(1)^0) -- string and not utf !

function string.totable(str)
    return lpegmatch(pattern,str)
end

-- handy from within tex:

local replacer = lpeg.replacer("@","%%") -- Watch the escaped % in lpeg!

function string.tformat(fmt,...)
    return format(lpegmatch(replacer,fmt),...)
end

-- obsolete names:

string.quote   = string.quoted
string.unquote = string.unquoted
