summaryrefslogtreecommitdiff
path: root/scripts
diff options
context:
space:
mode:
authorHans Hagen <pragma@wxs.nl>2012-12-10 23:20:00 +0100
committerHans Hagen <pragma@wxs.nl>2012-12-10 23:20:00 +0100
commitd2d2f5a76a2323d1adafe3dc1926e95064b206d9 (patch)
tree03582051cf1ffb8460a9f143f875e893fb849512 /scripts
parent898996a6e7727a0530533135ce19570647c924fb (diff)
downloadcontext-d2d2f5a76a2323d1adafe3dc1926e95064b206d9.tar.gz
beta 2012.12.10 23:20
Diffstat (limited to 'scripts')
-rw-r--r--scripts/context/lua/mtxrun.lua571
-rw-r--r--scripts/context/stubs/mswin/mtxrun.lua571
-rwxr-xr-xscripts/context/stubs/unix/mtxrun571
3 files changed, 831 insertions, 882 deletions
diff --git a/scripts/context/lua/mtxrun.lua b/scripts/context/lua/mtxrun.lua
index be3acb7da..01c601eb5 100644
--- a/scripts/context/lua/mtxrun.lua
+++ b/scripts/context/lua/mtxrun.lua
@@ -1625,7 +1625,8 @@ function lpeg.replacer(one,two,makefunction)
elseif no == 1 then
local o = one[1]
one, two = P(o[1]), o[2]
- pattern = Cs(((1-one)^1 + one/two)^0)
+ -- pattern = Cs(((1-one)^1 + one/two)^0)
+ pattern = Cs((one/two + 1)^0)
else
for i=1,no do
local o = one[i]
@@ -1636,7 +1637,28 @@ function lpeg.replacer(one,two,makefunction)
else
one = P(one)
two = two or ""
- pattern = Cs(((1-one)^1 + one/two)^0)
+ -- pattern = Cs(((1-one)^1 + one/two)^0)
+ pattern = Cs((one/two +1)^0)
+ end
+ if makefunction then
+ return function(str)
+ return lpegmatch(pattern,str)
+ end
+ else
+ return pattern
+ end
+end
+
+function lpeg.finder(lst,makefunction)
+ local pattern
+ if type(lst) == "table" then
+ local p = P(false)
+ for i=1,#lst do
+ p = p + P(lst[i])
+ end
+ pattern = (p + 1)^0
+ else
+ pattern = (P(lst) + 1)^0
end
if makefunction then
return function(str)
@@ -3058,66 +3080,145 @@ file = file or { }
local file = file
local insert, concat = table.insert, table.concat
-local find, gmatch, match, gsub, sub, char, lower = string.find, string.gmatch, string.match, string.gsub, string.sub, string.char, string.lower
+local match = string.match
local lpegmatch = lpeg.match
local getcurrentdir, attributes = lfs.currentdir, lfs.attributes
+local checkedsplit = string.checkedsplit
+
+-- local patterns = file.patterns or { }
+-- file.patterns = patterns
-local P, R, S, C, Cs, Cp, Cc = lpeg.P, lpeg.R, lpeg.S, lpeg.C, lpeg.Cs, lpeg.Cp, lpeg.Cc
+local P, R, S, C, Cs, Cp, Cc, Ct = lpeg.P, lpeg.R, lpeg.S, lpeg.C, lpeg.Cs, lpeg.Cp, lpeg.Cc, lpeg.Ct
-local function dirname(name,default)
- return match(name,"^(.+)[/\\].-$") or (default or "")
+local colon = P(":")
+local period = P(".")
+local periods = P("..")
+local fwslash = P("/")
+local bwslash = P("\\")
+local slashes = S("\\/")
+local noperiod = 1-period
+local noslashes = 1-slashes
+local name = noperiod^1
+local suffix = period/"" * (1-period-slashes)^1 * -1
+
+local pattern = C((noslashes^0 * slashes^1)^1)
+
+local function pathpart(name,default)
+ return lpegmatch(pattern,name) or default or ""
end
+local pattern = (noslashes^0 * slashes)^1 * C(noslashes^1) * -1
+
local function basename(name)
- return match(name,"^.+[/\\](.-)$") or name
+ return lpegmatch(pattern,name) or name
end
--- local function nameonly(name)
--- return (gsub(match(name,"^.+[/\\](.-)$") or name,"%..*$",""))
--- end
+local pattern = (noslashes^0 * slashes^1)^0 * Cs((1-suffix)^1) * suffix^0
local function nameonly(name)
- return (gsub(match(name,"^.+[/\\](.-)$") or name,"%.[%a%d]+$",""))
+ return lpegmatch(pattern,name) or name
end
-local function suffixonly(name,default)
- return match(name,"^.+%.([^/\\]-)$") or default or ""
-end
+local pattern = (noslashes^0 * slashes)^0 * (noperiod^1 * period)^1 * C(noperiod^1) * -1
-local function splitname(name)
- local n, s = match(name,"^(.+)%.([^/\\]-)$")
- return n or name, s or ""
+local function suffixonly(name)
+ return lpegmatch(pattern,name) or ""
end
+file.pathpart = pathpart
file.basename = basename
-
-file.pathpart = dirname
-file.dirname = dirname
-
file.nameonly = nameonly
-
file.suffixonly = suffixonly
-file.extname = suffixonly -- obsolete
file.suffix = suffixonly
-function file.removesuffix(filename)
- return (gsub(filename,"%.[%a%d]+$",""))
+file.dirname = pathpart -- obsolete
+file.extname = suffixonly -- obsolete
+
+-- actually these are schemes
+
+local drive = C(R("az","AZ")) * colon
+local path = C(((1-slashes)^0 * slashes)^0)
+local suffix = period * C(P(1-period)^0 * P(-1))
+local base = C((1-suffix)^0)
+local rest = C(P(1)^0)
+
+drive = drive + Cc("")
+path = path + Cc("")
+base = base + Cc("")
+suffix = suffix + Cc("")
+
+local pattern_a = drive * path * base * suffix
+local pattern_b = path * base * suffix
+local pattern_c = C(drive * path) * C(base * suffix) -- trick: two extra captures
+local pattern_d = path * rest
+
+function file.splitname(str,splitdrive)
+ if splitdrive then
+ return lpegmatch(pattern_a,str) -- returns drive, path, base, suffix
+ else
+ return lpegmatch(pattern_b,str) -- returns path, base, suffix
+ end
+end
+
+function file.splitbase(str)
+ return lpegmatch(pattern_d,str) -- returns path, base+suffix
+end
+
+function file.nametotable(str,splitdrive) -- returns table
+ local path, drive, subpath, name, base, suffix = lpegmatch(pattern_c,str)
+ if splitdrive then
+ return {
+ path = path,
+ drive = drive,
+ subpath = subpath,
+ name = name,
+ base = base,
+ suffix = suffix,
+ }
+ else
+ return {
+ path = path,
+ name = name,
+ base = base,
+ suffix = suffix,
+ }
+ end
+end
+
+local pattern = Cs(((period * noperiod^1 * -1)/"" + 1)^1)
+
+function file.removesuffix(name)
+ return lpegmatch(pattern,name)
end
+-- local pattern = (noslashes^0 * slashes)^0 * (noperiod^1 * period)^1 * Cp() * noperiod^1 * -1
+--
+-- function file.addsuffix(name, suffix)
+-- local p = lpegmatch(pattern,name)
+-- if p then
+-- return name
+-- else
+-- return name .. "." .. suffix
+-- end
+-- end
+
+local suffix = period/"" * (1-period-slashes)^1 * -1
+local pattern = Cs((noslashes^0 * slashes^1)^0 * ((1-suffix)^1)) * Cs(suffix)
+
function file.addsuffix(filename, suffix, criterium)
if not suffix or suffix == "" then
return filename
elseif criterium == true then
return filename .. "." .. suffix
elseif not criterium then
- local n, s = splitname(filename)
+ local n, s = lpegmatch(pattern,filename)
if not s or s == "" then
return filename .. "." .. suffix
else
return filename
end
else
- local n, s = splitname(filename)
+ local n, s = lpegmatch(pattern,filename)
if s and s ~= "" then
local t = type(criterium)
if t == "table" then
@@ -3134,88 +3235,49 @@ function file.addsuffix(filename, suffix, criterium)
end
end
end
- return n .. "." .. suffix
+ return (n or filename) .. "." .. suffix
end
end
+-- print("1 " .. file.addsuffix("name","new") .. " -> name.new")
+-- print("2 " .. file.addsuffix("name.old","new") .. " -> name.old")
+-- print("3 " .. file.addsuffix("name.old","new",true) .. " -> name.old.new")
+-- print("4 " .. file.addsuffix("name.old","new","new") .. " -> name.new")
+-- print("5 " .. file.addsuffix("name.old","new","old") .. " -> name.old")
+-- print("6 " .. file.addsuffix("name.old","new","foo") .. " -> name.new")
+-- print("7 " .. file.addsuffix("name.old","new",{"foo","bar"}) .. " -> name.new")
+-- print("8 " .. file.addsuffix("name.old","new",{"old","bar"}) .. " -> name.old")
-function file.replacesuffix(filename, suffix)
- return (gsub(filename,"%.[%a%d]+$","")) .. "." .. suffix
+local suffix = period * (1-period-slashes)^1 * -1
+local pattern = Cs((1-suffix)^0)
+
+function file.replacesuffix(name,suffix)
+ if suffix and suffix ~= "" then
+ return lpegmatch(pattern,name) .. "." .. suffix
+ else
+ return name
+ end
end
-local trick_1 = char(1)
-local trick_2 = "^" .. trick_1 .. "/+"
+--
-function file.join(...) -- rather dirty
- local lst = { ... }
- local a, b = lst[1], lst[2]
- if not a or a == "" then -- not a added
- lst[1] = trick_1
- elseif b and find(a,"^/+$") and find(b,"^/") then
- lst[1] = ""
- lst[2] = gsub(b,"^/+","")
- end
- local pth = concat(lst,"/")
- pth = gsub(pth,"\\","/")
- local a, b = match(pth,"^(.*://)(.*)$")
- if a and b then
- return a .. gsub(b,"//+","/")
- end
- a, b = match(pth,"^(//)(.*)$")
- if a and b then
- return a .. gsub(b,"//+","/")
- end
- pth = gsub(pth,trick_2,"")
- return (gsub(pth,"//+","/"))
-end
-
--- local slash = P("/")
--- local colon = P(":")
-
--- local replacer = lpeg.replacer(S("\\/")^1,"/")
--- local stripper = Cs(P(slash)^0/"" * replacer)
--- local isnetwork = slash * slash * (1-slash) + (1-slash-colon)^1 * colon
--- local isroot = slash^1 * -1
--- local hasroot = slash^1
-
--- function file.newjoin(...) -- rather dirty
--- local lst = { ... }
--- local one = lst[1]
--- if lpegmatch(isnetwork,one) then
--- local two = lpegmatch(replacer,concat(lst,"/",2))
--- return one .. two
--- elseif lpegmatch(isroot,one) then
--- local two = lpegmatch(replacer,concat(lst,"/",2))
--- if lpegmatch(hasroot,two) then
--- return two
--- else
--- return "/" .. two
--- end
--- elseif one == "" then
--- return lpegmatch(stripper,concat(lst,"/",2))
--- else
--- return lpegmatch(replacer,concat(lst,"/"))
--- end
--- end
+local reslasher = lpeg.replacer(S("\\"),"/")
--- print(file.join("//","/y"))
--- print(file.join("/","/y"))
--- print(file.join("","/y"))
--- print(file.join("/x/","/y"))
--- print(file.join("x/","/y"))
--- print(file.join("http://","/y"))
--- print(file.join("http://a","/y"))
--- print(file.join("http:///a","/y"))
--- print(file.join("//nas-1","/y"))
+function file.reslash(str)
+ return lpegmatch(reslasher,str)
+end
-- We should be able to use:
--
+-- local writable = P(1) * P("w") * Cc(true)
+--
-- function file.is_writable(name)
--- local a = attributes(name) or attributes(dirname(name,"."))
--- return a and sub(a.permissions,2,2) == "w"
+-- local a = attributes(name) or attributes(pathpart(name,"."))
+-- return a and lpegmatch(writable,a.permissions) or false
-- end
--
--- But after some testing Taco and I came up with:
+-- But after some testing Taco and I came up with the more robust
+-- variant:
function file.is_writable(name)
if lfs.isdir(name) then
@@ -3243,9 +3305,11 @@ function file.is_writable(name)
return false
end
+local readable = P("r") * Cc(true)
+
function file.is_readable(name)
local a = attributes(name)
- return a and sub(a.permissions,1,1) == "r"
+ return a and lpegmatch(readable,a.permissions) or false
end
file.isreadable = file.is_readable -- depricated
@@ -3256,41 +3320,74 @@ function file.size(name)
return a and a.size or 0
end
--- todo: lpeg \\ / .. does not save much
-
-local checkedsplit = string.checkedsplit
-
-function file.splitpath(str,separator) -- string
- str = gsub(str,"\\","/")
- return checkedsplit(str,separator or io.pathseparator)
+function file.splitpath(str,separator) -- string .. reslash is a bonus (we could do a direct split)
+ return checkedsplit(lpegmatch(reslasher,str),separator or io.pathseparator)
end
function file.joinpath(tab,separator) -- table
return concat(tab,separator or io.pathseparator) -- can have trailing //
end
--- we can hash them weakly
+local stripper = Cs(P(fwslash)^0/"" * reslasher)
+local isnetwork = fwslash * fwslash * (1-fwslash) + (1-fwslash-colon)^1 * colon
+local isroot = fwslash^1 * -1
+local hasroot = fwslash^1
+
+function file.join(...) -- rather dirty
+ local lst = { ... }
+ local one = lst[1]
+ if lpegmatch(isnetwork,one) then
+ local two = lpegmatch(reslasher,concat(lst,"/",2))
+ return one .. "/" .. two
+ elseif lpegmatch(isroot,one) then
+ local two = lpegmatch(reslasher,concat(lst,"/",2))
+ if lpegmatch(hasroot,two) then
+ return two
+ else
+ return "/" .. two
+ end
+ elseif one == "" then
+ return lpegmatch(stripper,concat(lst,"/",2))
+ else
+ return lpegmatch(reslasher,concat(lst,"/"))
+ end
+end
+
+-- print(file.join("c:/whatever","name"))
+-- print(file.join("//","/y"))
+-- print(file.join("/","/y"))
+-- print(file.join("","/y"))
+-- print(file.join("/x/","/y"))
+-- print(file.join("x/","/y"))
+-- print(file.join("http://","/y"))
+-- print(file.join("http://a","/y"))
+-- print(file.join("http:///a","/y"))
+-- print(file.join("//nas-1","/y"))
+
+-- The previous one fails on "a.b/c" so Taco came up with a split based
+-- variant. After some skyping we got it sort of compatible with the old
+-- one. After that the anchoring to currentdir was added in a better way.
+-- Of course there are some optimizations too. Finally we had to deal with
+-- windows drive prefixes and things like sys://. Eventually gsubs and
+-- finds were replaced by lpegs.
+local drivespec = R("az","AZ")^1 * colon
+local anchors = fwslash + drivespec
+local untouched = periods + (1-period)^1 * P(-1)
+local splitstarter = (Cs(drivespec * (bwslash/"/" + fwslash)^0) + Cc(false)) * Ct(lpeg.splitat(S("/\\")^1))
+local absolute = fwslash
function file.collapsepath(str,anchor)
- if anchor and not find(str,"^/") and not find(str,"^%a:") then
+ if anchor and not lpegmatch(anchors,str) then
str = getcurrentdir() .. "/" .. str
end
if str == "" or str =="." then
return "."
- elseif find(str,"^%.%.") then
- str = gsub(str,"\\","/")
- return str
- elseif not find(str,"%.") then
- str = gsub(str,"\\","/")
- return str
- end
- str = gsub(str,"\\","/")
- local starter, rest = match(str,"^(%a+:/*)(.-)$")
- if starter then
- str = rest
+ elseif lpegmatch(untouched,str) then
+ return lpegmatch(reslasher,str)
end
- local oldelements = checkedsplit(str,"/")
+ local starter, oldelements = lpegmatch(splitstarter,str)
+-- inspect(oldelements)
local newelements = { }
local i = #oldelements
while i > 0 do
@@ -3320,7 +3417,7 @@ function file.collapsepath(str,anchor)
return starter or "."
elseif starter then
return starter .. concat(newelements, '/')
- elseif find(str,"^/") then
+ elseif lpegmatch(absolute,str) then
return "/" .. concat(newelements,'/')
else
return concat(newelements, '/')
@@ -3337,29 +3434,21 @@ end
-- test("a/./b/..") test("a/aa/../b/bb") test("a/.././././b/..") test("a/./././b/..")
-- test("a/b/c/../..") test("./a/b/c/../..") test("a/b/c/../..")
+local validchars = R("az","09","AZ","--","..")
+local pattern_a = lpeg.replacer(1-validchars)
+local pattern_a = Cs((validchars + P(1)/"-")^1)
+local whatever = P("-")^0 / ""
+local pattern_b = Cs(whatever * (1 - whatever * -1)^1)
+
function file.robustname(str,strict)
- str = gsub(str,"[^%a%d%/%-%.\\]+","-")
+ str = lpegmatch(pattern_a,str) or str
if strict then
- return lower(gsub(str,"^%-*(.-)%-*$","%1"))
+ return lpegmatch(pattern_b,str) or str -- two step is cleaner (less backtracking)
else
return str
end
end
--- local pattern_a = lpeg.replacer(1-R("az","09","AZ","--",".."))
--- local pattern_a = Cs((R("az","09","AZ","--","..") + P(1)/"-")^1)
--- local whatever = P("-")^0 / ""
--- local pattern_b = Cs(whatever * (1 - whatever * -1)^1)
-
--- function file.robustname(str,strict)
--- str = lpegmatch(pattern_a,str) or str
--- if strict then
--- return lpegmatch(pattern_b,str) or str -- two step is cleaner (less backtracking)
--- else
--- return str
--- end
--- end
-
file.readdata = io.loaddata
file.savedata = io.savedata
@@ -3367,92 +3456,17 @@ function file.copy(oldname,newname)
file.savedata(newname,io.loaddata(oldname))
end
--- lpeg variants, slightly faster, not always
-
--- local period = P(".")
--- local slashes = S("\\/")
--- local noperiod = 1-period
--- local noslashes = 1-slashes
--- local name = noperiod^1
-
--- local pattern = (noslashes^0 * slashes)^0 * (noperiod^1 * period)^1 * C(noperiod^1) * -1
-
--- function file.suffixonly(name)
--- return lpegmatch(pattern,name) or ""
--- end
-
--- local pattern = Cs(((period * noperiod^1 * -1)/"" + 1)^1)
-
--- function file.removesuffix(name)
--- return lpegmatch(pattern,name)
--- end
-
--- local pattern = (noslashes^0 * slashes)^1 * C(noslashes^1) * -1
-
--- function file.basename(name)
--- return lpegmatch(pattern,name) or name
--- end
-
--- local pattern = Cs ((1 - slashes * noslashes^1 * -1)^1)
-
--- function file.dirname(name)
--- return lpegmatch(pattern,name) or ""
--- end
-
--- local pattern = (noslashes^0 * slashes)^0 * (noperiod^1 * period)^1 * Cp() * noperiod^1 * -1
-
--- function file.addsuffix(name, suffix)
--- local p = lpegmatch(pattern,name)
--- if p then
--- return name
--- else
--- return name .. "." .. suffix
--- end
--- end
-
--- local suffix = period * (1-period-slashes)^1 * -1
--- local pattern = Cs((1-suffix)^1)
-
--- function file.replacesuffix(name,suffix)
--- if suffix and suffix ~= "" then
--- return lpegmatch(pattern,name) .. "." .. suffix
--- else
--- return name
--- end
--- end
-
--- local path = noslashes^0 * slashes^1
--- local suffix = period * (1-period-slashes)^1 * -1
--- local pattern = path^0 * Cs((1-suffix)^1) * suffix^0
-
--- function file.nameonly(name)
--- return lpegmatch(pattern,name) or name
--- end
-
--- local test = file.suffixonly
--- local test = file.basename
--- local test = file.dirname
--- local test = file.addsuffix
--- local test = file.replacesuffix
--- local test = file.nameonly
-
--- print(1,test("./a/b/c/abd.def.xxx","!!!"))
--- print(2,test("./../b/c/abd.def.xxx","!!!"))
--- print(3,test("a/b/c/abd.def.xxx","!!!"))
--- print(4,test("a/b/c/def.xxx","!!!"))
--- print(5,test("a/b/c/def","!!!"))
--- print(6,test("def","!!!"))
--- print(7,test("def.xxx","!!!"))
-
--- local tim = os.clock() for i=1,250000 do local ext = test("abd.def.xxx","!!!") end print(os.clock()-tim)
-
-- also rewrite previous
local letter = R("az","AZ") + S("_-+")
local separator = P("://")
-local qualified = P(".")^0 * P("/") + letter*P(":") + letter^1*separator + letter^1 * P("/")
-local rootbased = P("/") + letter*P(":")
+local qualified = period^0 * fwslash
+ + letter * colon
+ + letter^1 * separator
+ + letter^1 * fwslash
+local rootbased = fwslash
+ + letter * colon
lpeg.patterns.qualified = qualified
lpeg.patterns.rootbased = rootbased
@@ -3467,61 +3481,6 @@ function file.is_rootbased_path(filename)
return lpegmatch(rootbased,filename) ~= nil
end
--- actually these are schemes
-
-local slash = S("\\/")
-local period = P(".")
-local drive = C(R("az","AZ")) * P(":")
-local path = C(((1-slash)^0 * slash)^0)
-local suffix = period * C(P(1-period)^0 * P(-1))
-local base = C((1-suffix)^0)
-local rest = C(P(1)^0)
-
-drive = drive + Cc("")
-path = path + Cc("")
-base = base + Cc("")
-suffix = suffix + Cc("")
-
-local pattern_a = drive * path * base * suffix
-local pattern_b = path * base * suffix
-local pattern_c = C(drive * path) * C(base * suffix) -- trick: two extra captures
-local pattern_d = path * rest
-
-function file.splitname(str,splitdrive)
- if splitdrive then
- return lpegmatch(pattern_a,str) -- returns drive, path, base, suffix
- else
- return lpegmatch(pattern_b,str) -- returns path, base, suffix
- end
-end
-
-function file.splitbase(str)
- return lpegmatch(pattern_d,str) -- returns path, base+suffix
-end
-
-function file.nametotable(str,splitdrive) -- returns table
- local path, drive, subpath, name, base, suffix = lpegmatch(pattern_c,str)
- if splitdrive then
- return {
- path = path,
- drive = drive,
- subpath = subpath,
- name = name,
- base = base,
- suffix = suffix,
- }
- else
- return {
- path = path,
- name = name,
- base = base,
- suffix = suffix,
- }
- end
-end
-
--- print(file.splitbase("a/b/c.txt"))
-
-- function test(t) for k, v in next, t do print(v, "=>", file.splitname(v)) end end
--
-- test { "c:", "c:/aa", "c:/aa/bb", "c:/aa/bb/cc", "c:/aa/bb/cc.dd", "c:/aa/bb/cc.dd.ee" }
@@ -3529,6 +3488,14 @@ end
-- test { "/aa", "/aa/bb", "/aa/bb/cc", "/aa/bb/cc.dd", "/aa/bb/cc.dd.ee" }
-- test { "aa", "aa/bb", "aa/bb/cc", "aa/bb/cc.dd", "aa/bb/cc.dd.ee" }
+-- -- maybe:
+--
+-- if os.type == "windows" then
+-- local currentdir = getcurrentdir
+-- function getcurrentdir()
+-- return lpegmatch(reslasher,currentdir())
+-- end
+-- end
-- for myself:
@@ -3537,6 +3504,21 @@ function file.strip(name,dir)
return a ~= "" and a or name
end
+-- local debuglist = {
+-- "pathpart", "basename", "nameonly", "suffixonly", "suffix", "dirname", "extname",
+-- "addsuffix", "removesuffix", "replacesuffix", "join",
+-- "strip","collapsepath", "joinpath", "splitpath",
+-- }
+
+-- for i=1,#debuglist do
+-- local name = debuglist[i]
+-- local f = file[name]
+-- file[name] = function(...)
+-- print(name,f(...))
+-- return f(...)
+-- end
+-- end
+
end -- of closure
@@ -3661,7 +3643,7 @@ if not modules then modules = { } end modules ['l-url'] = {
license = "see context related readme files"
}
-local char, gmatch, gsub, format, byte, find = string.char, string.gmatch, string.gsub, string.format, string.byte, string.find
+local char, format, byte = string.char, string.format, string.byte
local concat = table.concat
local tonumber, type = tonumber, type
local P, C, R, S, Cs, Cc, Ct, Cf, Cg, V = lpeg.P, lpeg.C, lpeg.R, lpeg.S, lpeg.Cs, lpeg.Cc, lpeg.Ct, lpeg.Cf, lpeg.Cg, lpeg.V
@@ -3700,6 +3682,8 @@ local nothing = Cc("")
local escapedchar = (percent * C(hexdigit * hexdigit)) / tochar
local escaped = (plus / " ") + escapedchar
+local noslash = P("/") / ""
+
-- we assume schemes with more than 1 character (in order to avoid problems with windows disks)
-- we also assume that when we have a scheme, we also have an authority
--
@@ -3878,29 +3862,23 @@ function url.construct(hash) -- dodo: we need to escape !
return lpegmatch(escaper,concat(fullurl))
end
-function url.filename(filename) -- why no lpeg here ?
- local t = hashed(filename)
- return (t.scheme == "file" and (gsub(t.path,"^/([a-zA-Z])([:|])/)","%1:"))) or filename
+local pattern = Cs(noslash * R("az","AZ") * (S(":|")/":") * noslash * P(1)^0)
+
+function url.filename(filename)
+ local spec = hashed(filename)
+ local path = spec.path
+ return (spec.scheme == "file" and path and lpegmatch(pattern,path)) or filename
end
+-- print(url.filename("/c|/test"))
+-- print(url.filename("/c/test"))
+
local function escapestring(str)
return lpegmatch(escaper,str)
end
url.escape = escapestring
--- function url.query(str) -- separator could be an option
--- if type(str) == "string" then
--- local t = { }
--- for k, v in gmatch(str,"([^&=]*)=([^&=]*)") do
--- t[k] = v
--- end
--- return t
--- else
--- return str
--- end
--- end
-
function url.query(str)
if type(str) == "string" then
return lpegmatch(splitquery,str) or ""
@@ -3928,14 +3906,19 @@ end
-- /test/ | /test | test/ | test => test
+local pattern = Cs(noslash^0 * (1 - noslash * P(-1))^0)
+
function url.barepath(path)
if not path or path == "" then
return ""
else
- return (gsub(path,"^/?(.-)/?$","%1"))
+ return lpegmatch(pattern,path)
end
end
+-- print(url.barepath("/test"),url.barepath("test/"),url.barepath("/test/"),url.barepath("test"))
+-- print(url.barepath("/x/yz"),url.barepath("x/yz/"),url.barepath("/x/yz/"),url.barepath("x/yz"))
+
@@ -6191,8 +6174,8 @@ end
-- for chem (currently one level)
local value = P(lbrace * C((nobrace + nestedbraces)^0) * rbrace)
- + C(digit^1 * lparent * (noparent + nestedparents)^0 * rparent)
- + C((nestedbraces + (1-comma))^0)
+ + C(digit^1 * lparent * (noparent + nestedparents)^1 * rparent)
+ + C((nestedbraces + (1-comma))^1)
local pattern_a = spaces * Ct(value*(separator*value)^0)
local function repeater(n,str)
@@ -6216,15 +6199,15 @@ local function repeater(n,str)
end
local value = P(lbrace * C((nobrace + nestedbraces)^0) * rbrace)
- + (C(digit^1)/tonumber * lparent * Cs((noparent + nestedparents)^0) * rparent) / repeater
- + C((nestedbraces + (1-comma))^0)
+ + (C(digit^1)/tonumber * lparent * Cs((noparent + nestedparents)^1) * rparent) / repeater
+ + C((nestedbraces + (1-comma))^1)
local pattern_b = spaces * Ct(value*(separator*value)^0)
-function parsers.settings_to_array_with_repeat(str,expand)
+function parsers.settings_to_array_with_repeat(str,expand) -- beware: "" => { }
if expand then
- return lpegmatch(pattern_b,str)
+ return lpegmatch(pattern_b,str) or { }
else
- return lpegmatch(pattern_a,str)
+ return lpegmatch(pattern_a,str) or { }
end
end
diff --git a/scripts/context/stubs/mswin/mtxrun.lua b/scripts/context/stubs/mswin/mtxrun.lua
index be3acb7da..01c601eb5 100644
--- a/scripts/context/stubs/mswin/mtxrun.lua
+++ b/scripts/context/stubs/mswin/mtxrun.lua
@@ -1625,7 +1625,8 @@ function lpeg.replacer(one,two,makefunction)
elseif no == 1 then
local o = one[1]
one, two = P(o[1]), o[2]
- pattern = Cs(((1-one)^1 + one/two)^0)
+ -- pattern = Cs(((1-one)^1 + one/two)^0)
+ pattern = Cs((one/two + 1)^0)
else
for i=1,no do
local o = one[i]
@@ -1636,7 +1637,28 @@ function lpeg.replacer(one,two,makefunction)
else
one = P(one)
two = two or ""
- pattern = Cs(((1-one)^1 + one/two)^0)
+ -- pattern = Cs(((1-one)^1 + one/two)^0)
+ pattern = Cs((one/two +1)^0)
+ end
+ if makefunction then
+ return function(str)
+ return lpegmatch(pattern,str)
+ end
+ else
+ return pattern
+ end
+end
+
+function lpeg.finder(lst,makefunction)
+ local pattern
+ if type(lst) == "table" then
+ local p = P(false)
+ for i=1,#lst do
+ p = p + P(lst[i])
+ end
+ pattern = (p + 1)^0
+ else
+ pattern = (P(lst) + 1)^0
end
if makefunction then
return function(str)
@@ -3058,66 +3080,145 @@ file = file or { }
local file = file
local insert, concat = table.insert, table.concat
-local find, gmatch, match, gsub, sub, char, lower = string.find, string.gmatch, string.match, string.gsub, string.sub, string.char, string.lower
+local match = string.match
local lpegmatch = lpeg.match
local getcurrentdir, attributes = lfs.currentdir, lfs.attributes
+local checkedsplit = string.checkedsplit
+
+-- local patterns = file.patterns or { }
+-- file.patterns = patterns
-local P, R, S, C, Cs, Cp, Cc = lpeg.P, lpeg.R, lpeg.S, lpeg.C, lpeg.Cs, lpeg.Cp, lpeg.Cc
+local P, R, S, C, Cs, Cp, Cc, Ct = lpeg.P, lpeg.R, lpeg.S, lpeg.C, lpeg.Cs, lpeg.Cp, lpeg.Cc, lpeg.Ct
-local function dirname(name,default)
- return match(name,"^(.+)[/\\].-$") or (default or "")
+local colon = P(":")
+local period = P(".")
+local periods = P("..")
+local fwslash = P("/")
+local bwslash = P("\\")
+local slashes = S("\\/")
+local noperiod = 1-period
+local noslashes = 1-slashes
+local name = noperiod^1
+local suffix = period/"" * (1-period-slashes)^1 * -1
+
+local pattern = C((noslashes^0 * slashes^1)^1)
+
+local function pathpart(name,default)
+ return lpegmatch(pattern,name) or default or ""
end
+local pattern = (noslashes^0 * slashes)^1 * C(noslashes^1) * -1
+
local function basename(name)
- return match(name,"^.+[/\\](.-)$") or name
+ return lpegmatch(pattern,name) or name
end
--- local function nameonly(name)
--- return (gsub(match(name,"^.+[/\\](.-)$") or name,"%..*$",""))
--- end
+local pattern = (noslashes^0 * slashes^1)^0 * Cs((1-suffix)^1) * suffix^0
local function nameonly(name)
- return (gsub(match(name,"^.+[/\\](.-)$") or name,"%.[%a%d]+$",""))
+ return lpegmatch(pattern,name) or name
end
-local function suffixonly(name,default)
- return match(name,"^.+%.([^/\\]-)$") or default or ""
-end
+local pattern = (noslashes^0 * slashes)^0 * (noperiod^1 * period)^1 * C(noperiod^1) * -1
-local function splitname(name)
- local n, s = match(name,"^(.+)%.([^/\\]-)$")
- return n or name, s or ""
+local function suffixonly(name)
+ return lpegmatch(pattern,name) or ""
end
+file.pathpart = pathpart
file.basename = basename
-
-file.pathpart = dirname
-file.dirname = dirname
-
file.nameonly = nameonly
-
file.suffixonly = suffixonly
-file.extname = suffixonly -- obsolete
file.suffix = suffixonly
-function file.removesuffix(filename)
- return (gsub(filename,"%.[%a%d]+$",""))
+file.dirname = pathpart -- obsolete
+file.extname = suffixonly -- obsolete
+
+-- actually these are schemes
+
+local drive = C(R("az","AZ")) * colon
+local path = C(((1-slashes)^0 * slashes)^0)
+local suffix = period * C(P(1-period)^0 * P(-1))
+local base = C((1-suffix)^0)
+local rest = C(P(1)^0)
+
+drive = drive + Cc("")
+path = path + Cc("")
+base = base + Cc("")
+suffix = suffix + Cc("")
+
+local pattern_a = drive * path * base * suffix
+local pattern_b = path * base * suffix
+local pattern_c = C(drive * path) * C(base * suffix) -- trick: two extra captures
+local pattern_d = path * rest
+
+function file.splitname(str,splitdrive)
+ if splitdrive then
+ return lpegmatch(pattern_a,str) -- returns drive, path, base, suffix
+ else
+ return lpegmatch(pattern_b,str) -- returns path, base, suffix
+ end
+end
+
+function file.splitbase(str)
+ return lpegmatch(pattern_d,str) -- returns path, base+suffix
+end
+
+function file.nametotable(str,splitdrive) -- returns table
+ local path, drive, subpath, name, base, suffix = lpegmatch(pattern_c,str)
+ if splitdrive then
+ return {
+ path = path,
+ drive = drive,
+ subpath = subpath,
+ name = name,
+ base = base,
+ suffix = suffix,
+ }
+ else
+ return {
+ path = path,
+ name = name,
+ base = base,
+ suffix = suffix,
+ }
+ end
+end
+
+local pattern = Cs(((period * noperiod^1 * -1)/"" + 1)^1)
+
+function file.removesuffix(name)
+ return lpegmatch(pattern,name)
end
+-- local pattern = (noslashes^0 * slashes)^0 * (noperiod^1 * period)^1 * Cp() * noperiod^1 * -1
+--
+-- function file.addsuffix(name, suffix)
+-- local p = lpegmatch(pattern,name)
+-- if p then
+-- return name
+-- else
+-- return name .. "." .. suffix
+-- end
+-- end
+
+local suffix = period/"" * (1-period-slashes)^1 * -1
+local pattern = Cs((noslashes^0 * slashes^1)^0 * ((1-suffix)^1)) * Cs(suffix)
+
function file.addsuffix(filename, suffix, criterium)
if not suffix or suffix == "" then
return filename
elseif criterium == true then
return filename .. "." .. suffix
elseif not criterium then
- local n, s = splitname(filename)
+ local n, s = lpegmatch(pattern,filename)
if not s or s == "" then
return filename .. "." .. suffix
else
return filename
end
else
- local n, s = splitname(filename)
+ local n, s = lpegmatch(pattern,filename)
if s and s ~= "" then
local t = type(criterium)
if t == "table" then
@@ -3134,88 +3235,49 @@ function file.addsuffix(filename, suffix, criterium)
end
end
end
- return n .. "." .. suffix
+ return (n or filename) .. "." .. suffix
end
end
+-- print("1 " .. file.addsuffix("name","new") .. " -> name.new")
+-- print("2 " .. file.addsuffix("name.old","new") .. " -> name.old")
+-- print("3 " .. file.addsuffix("name.old","new",true) .. " -> name.old.new")
+-- print("4 " .. file.addsuffix("name.old","new","new") .. " -> name.new")
+-- print("5 " .. file.addsuffix("name.old","new","old") .. " -> name.old")
+-- print("6 " .. file.addsuffix("name.old","new","foo") .. " -> name.new")
+-- print("7 " .. file.addsuffix("name.old","new",{"foo","bar"}) .. " -> name.new")
+-- print("8 " .. file.addsuffix("name.old","new",{"old","bar"}) .. " -> name.old")
-function file.replacesuffix(filename, suffix)
- return (gsub(filename,"%.[%a%d]+$","")) .. "." .. suffix
+local suffix = period * (1-period-slashes)^1 * -1
+local pattern = Cs((1-suffix)^0)
+
+function file.replacesuffix(name,suffix)
+ if suffix and suffix ~= "" then
+ return lpegmatch(pattern,name) .. "." .. suffix
+ else
+ return name
+ end
end
-local trick_1 = char(1)
-local trick_2 = "^" .. trick_1 .. "/+"
+--
-function file.join(...) -- rather dirty
- local lst = { ... }
- local a, b = lst[1], lst[2]
- if not a or a == "" then -- not a added
- lst[1] = trick_1
- elseif b and find(a,"^/+$") and find(b,"^/") then
- lst[1] = ""
- lst[2] = gsub(b,"^/+","")
- end
- local pth = concat(lst,"/")
- pth = gsub(pth,"\\","/")
- local a, b = match(pth,"^(.*://)(.*)$")
- if a and b then
- return a .. gsub(b,"//+","/")
- end
- a, b = match(pth,"^(//)(.*)$")
- if a and b then
- return a .. gsub(b,"//+","/")
- end
- pth = gsub(pth,trick_2,"")
- return (gsub(pth,"//+","/"))
-end
-
--- local slash = P("/")
--- local colon = P(":")
-
--- local replacer = lpeg.replacer(S("\\/")^1,"/")
--- local stripper = Cs(P(slash)^0/"" * replacer)
--- local isnetwork = slash * slash * (1-slash) + (1-slash-colon)^1 * colon
--- local isroot = slash^1 * -1
--- local hasroot = slash^1
-
--- function file.newjoin(...) -- rather dirty
--- local lst = { ... }
--- local one = lst[1]
--- if lpegmatch(isnetwork,one) then
--- local two = lpegmatch(replacer,concat(lst,"/",2))
--- return one .. two
--- elseif lpegmatch(isroot,one) then
--- local two = lpegmatch(replacer,concat(lst,"/",2))
--- if lpegmatch(hasroot,two) then
--- return two
--- else
--- return "/" .. two
--- end
--- elseif one == "" then
--- return lpegmatch(stripper,concat(lst,"/",2))
--- else
--- return lpegmatch(replacer,concat(lst,"/"))
--- end
--- end
+local reslasher = lpeg.replacer(S("\\"),"/")
--- print(file.join("//","/y"))
--- print(file.join("/","/y"))
--- print(file.join("","/y"))
--- print(file.join("/x/","/y"))
--- print(file.join("x/","/y"))
--- print(file.join("http://","/y"))
--- print(file.join("http://a","/y"))
--- print(file.join("http:///a","/y"))
--- print(file.join("//nas-1","/y"))
+function file.reslash(str)
+ return lpegmatch(reslasher,str)
+end
-- We should be able to use:
--
+-- local writable = P(1) * P("w") * Cc(true)
+--
-- function file.is_writable(name)
--- local a = attributes(name) or attributes(dirname(name,"."))
--- return a and sub(a.permissions,2,2) == "w"
+-- local a = attributes(name) or attributes(pathpart(name,"."))
+-- return a and lpegmatch(writable,a.permissions) or false
-- end
--
--- But after some testing Taco and I came up with:
+-- But after some testing Taco and I came up with the more robust
+-- variant:
function file.is_writable(name)
if lfs.isdir(name) then
@@ -3243,9 +3305,11 @@ function file.is_writable(name)
return false
end
+local readable = P("r") * Cc(true)
+
function file.is_readable(name)
local a = attributes(name)
- return a and sub(a.permissions,1,1) == "r"
+ return a and lpegmatch(readable,a.permissions) or false
end
file.isreadable = file.is_readable -- depricated
@@ -3256,41 +3320,74 @@ function file.size(name)
return a and a.size or 0
end
--- todo: lpeg \\ / .. does not save much
-
-local checkedsplit = string.checkedsplit
-
-function file.splitpath(str,separator) -- string
- str = gsub(str,"\\","/")
- return checkedsplit(str,separator or io.pathseparator)
+function file.splitpath(str,separator) -- string .. reslash is a bonus (we could do a direct split)
+ return checkedsplit(lpegmatch(reslasher,str),separator or io.pathseparator)
end
function file.joinpath(tab,separator) -- table
return concat(tab,separator or io.pathseparator) -- can have trailing //
end
--- we can hash them weakly
+local stripper = Cs(P(fwslash)^0/"" * reslasher)
+local isnetwork = fwslash * fwslash * (1-fwslash) + (1-fwslash-colon)^1 * colon
+local isroot = fwslash^1 * -1
+local hasroot = fwslash^1
+
+function file.join(...) -- rather dirty
+ local lst = { ... }
+ local one = lst[1]
+ if lpegmatch(isnetwork,one) then
+ local two = lpegmatch(reslasher,concat(lst,"/",2))
+ return one .. "/" .. two
+ elseif lpegmatch(isroot,one) then
+ local two = lpegmatch(reslasher,concat(lst,"/",2))
+ if lpegmatch(hasroot,two) then
+ return two
+ else
+ return "/" .. two
+ end
+ elseif one == "" then
+ return lpegmatch(stripper,concat(lst,"/",2))
+ else
+ return lpegmatch(reslasher,concat(lst,"/"))
+ end
+end
+
+-- print(file.join("c:/whatever","name"))
+-- print(file.join("//","/y"))
+-- print(file.join("/","/y"))
+-- print(file.join("","/y"))
+-- print(file.join("/x/","/y"))
+-- print(file.join("x/","/y"))
+-- print(file.join("http://","/y"))
+-- print(file.join("http://a","/y"))
+-- print(file.join("http:///a","/y"))
+-- print(file.join("//nas-1","/y"))
+
+-- The previous one fails on "a.b/c" so Taco came up with a split based
+-- variant. After some skyping we got it sort of compatible with the old
+-- one. After that the anchoring to currentdir was added in a better way.
+-- Of course there are some optimizations too. Finally we had to deal with
+-- windows drive prefixes and things like sys://. Eventually gsubs and
+-- finds were replaced by lpegs.
+local drivespec = R("az","AZ")^1 * colon
+local anchors = fwslash + drivespec
+local untouched = periods + (1-period)^1 * P(-1)
+local splitstarter = (Cs(drivespec * (bwslash/"/" + fwslash)^0) + Cc(false)) * Ct(lpeg.splitat(S("/\\")^1))
+local absolute = fwslash
function file.collapsepath(str,anchor)
- if anchor and not find(str,"^/") and not find(str,"^%a:") then
+ if anchor and not lpegmatch(anchors,str) then
str = getcurrentdir() .. "/" .. str
end
if str == "" or str =="." then
return "."
- elseif find(str,"^%.%.") then
- str = gsub(str,"\\","/")
- return str
- elseif not find(str,"%.") then
- str = gsub(str,"\\","/")
- return str
- end
- str = gsub(str,"\\","/")
- local starter, rest = match(str,"^(%a+:/*)(.-)$")
- if starter then
- str = rest
+ elseif lpegmatch(untouched,str) then
+ return lpegmatch(reslasher,str)
end
- local oldelements = checkedsplit(str,"/")
+ local starter, oldelements = lpegmatch(splitstarter,str)
+-- inspect(oldelements)
local newelements = { }
local i = #oldelements
while i > 0 do
@@ -3320,7 +3417,7 @@ function file.collapsepath(str,anchor)
return starter or "."
elseif starter then
return starter .. concat(newelements, '/')
- elseif find(str,"^/") then
+ elseif lpegmatch(absolute,str) then
return "/" .. concat(newelements,'/')
else
return concat(newelements, '/')
@@ -3337,29 +3434,21 @@ end
-- test("a/./b/..") test("a/aa/../b/bb") test("a/.././././b/..") test("a/./././b/..")
-- test("a/b/c/../..") test("./a/b/c/../..") test("a/b/c/../..")
+local validchars = R("az","09","AZ","--","..")
+local pattern_a = lpeg.replacer(1-validchars)
+local pattern_a = Cs((validchars + P(1)/"-")^1)
+local whatever = P("-")^0 / ""
+local pattern_b = Cs(whatever * (1 - whatever * -1)^1)
+
function file.robustname(str,strict)
- str = gsub(str,"[^%a%d%/%-%.\\]+","-")
+ str = lpegmatch(pattern_a,str) or str
if strict then
- return lower(gsub(str,"^%-*(.-)%-*$","%1"))
+ return lpegmatch(pattern_b,str) or str -- two step is cleaner (less backtracking)
else
return str
end
end
--- local pattern_a = lpeg.replacer(1-R("az","09","AZ","--",".."))
--- local pattern_a = Cs((R("az","09","AZ","--","..") + P(1)/"-")^1)
--- local whatever = P("-")^0 / ""
--- local pattern_b = Cs(whatever * (1 - whatever * -1)^1)
-
--- function file.robustname(str,strict)
--- str = lpegmatch(pattern_a,str) or str
--- if strict then
--- return lpegmatch(pattern_b,str) or str -- two step is cleaner (less backtracking)
--- else
--- return str
--- end
--- end
-
file.readdata = io.loaddata
file.savedata = io.savedata
@@ -3367,92 +3456,17 @@ function file.copy(oldname,newname)
file.savedata(newname,io.loaddata(oldname))
end
--- lpeg variants, slightly faster, not always
-
--- local period = P(".")
--- local slashes = S("\\/")
--- local noperiod = 1-period
--- local noslashes = 1-slashes
--- local name = noperiod^1
-
--- local pattern = (noslashes^0 * slashes)^0 * (noperiod^1 * period)^1 * C(noperiod^1) * -1
-
--- function file.suffixonly(name)
--- return lpegmatch(pattern,name) or ""
--- end
-
--- local pattern = Cs(((period * noperiod^1 * -1)/"" + 1)^1)
-
--- function file.removesuffix(name)
--- return lpegmatch(pattern,name)
--- end
-
--- local pattern = (noslashes^0 * slashes)^1 * C(noslashes^1) * -1
-
--- function file.basename(name)
--- return lpegmatch(pattern,name) or name
--- end
-
--- local pattern = Cs ((1 - slashes * noslashes^1 * -1)^1)
-
--- function file.dirname(name)
--- return lpegmatch(pattern,name) or ""
--- end
-
--- local pattern = (noslashes^0 * slashes)^0 * (noperiod^1 * period)^1 * Cp() * noperiod^1 * -1
-
--- function file.addsuffix(name, suffix)
--- local p = lpegmatch(pattern,name)
--- if p then
--- return name
--- else
--- return name .. "." .. suffix
--- end
--- end
-
--- local suffix = period * (1-period-slashes)^1 * -1
--- local pattern = Cs((1-suffix)^1)
-
--- function file.replacesuffix(name,suffix)
--- if suffix and suffix ~= "" then
--- return lpegmatch(pattern,name) .. "." .. suffix
--- else
--- return name
--- end
--- end
-
--- local path = noslashes^0 * slashes^1
--- local suffix = period * (1-period-slashes)^1 * -1
--- local pattern = path^0 * Cs((1-suffix)^1) * suffix^0
-
--- function file.nameonly(name)
--- return lpegmatch(pattern,name) or name
--- end
-
--- local test = file.suffixonly
--- local test = file.basename
--- local test = file.dirname
--- local test = file.addsuffix
--- local test = file.replacesuffix
--- local test = file.nameonly
-
--- print(1,test("./a/b/c/abd.def.xxx","!!!"))
--- print(2,test("./../b/c/abd.def.xxx","!!!"))
--- print(3,test("a/b/c/abd.def.xxx","!!!"))
--- print(4,test("a/b/c/def.xxx","!!!"))
--- print(5,test("a/b/c/def","!!!"))
--- print(6,test("def","!!!"))
--- print(7,test("def.xxx","!!!"))
-
--- local tim = os.clock() for i=1,250000 do local ext = test("abd.def.xxx","!!!") end print(os.clock()-tim)
-
-- also rewrite previous
local letter = R("az","AZ") + S("_-+")
local separator = P("://")
-local qualified = P(".")^0 * P("/") + letter*P(":") + letter^1*separator + letter^1 * P("/")
-local rootbased = P("/") + letter*P(":")
+local qualified = period^0 * fwslash
+ + letter * colon
+ + letter^1 * separator
+ + letter^1 * fwslash
+local rootbased = fwslash
+ + letter * colon
lpeg.patterns.qualified = qualified
lpeg.patterns.rootbased = rootbased
@@ -3467,61 +3481,6 @@ function file.is_rootbased_path(filename)
return lpegmatch(rootbased,filename) ~= nil
end
--- actually these are schemes
-
-local slash = S("\\/")
-local period = P(".")
-local drive = C(R("az","AZ")) * P(":")
-local path = C(((1-slash)^0 * slash)^0)
-local suffix = period * C(P(1-period)^0 * P(-1))
-local base = C((1-suffix)^0)
-local rest = C(P(1)^0)
-
-drive = drive + Cc("")
-path = path + Cc("")
-base = base + Cc("")
-suffix = suffix + Cc("")
-
-local pattern_a = drive * path * base * suffix
-local pattern_b = path * base * suffix
-local pattern_c = C(drive * path) * C(base * suffix) -- trick: two extra captures
-local pattern_d = path * rest
-
-function file.splitname(str,splitdrive)
- if splitdrive then
- return lpegmatch(pattern_a,str) -- returns drive, path, base, suffix
- else
- return lpegmatch(pattern_b,str) -- returns path, base, suffix
- end
-end
-
-function file.splitbase(str)
- return lpegmatch(pattern_d,str) -- returns path, base+suffix
-end
-
-function file.nametotable(str,splitdrive) -- returns table
- local path, drive, subpath, name, base, suffix = lpegmatch(pattern_c,str)
- if splitdrive then
- return {
- path = path,
- drive = drive,
- subpath = subpath,
- name = name,
- base = base,
- suffix = suffix,
- }
- else
- return {
- path = path,
- name = name,
- base = base,
- suffix = suffix,
- }
- end
-end
-
--- print(file.splitbase("a/b/c.txt"))
-
-- function test(t) for k, v in next, t do print(v, "=>", file.splitname(v)) end end
--
-- test { "c:", "c:/aa", "c:/aa/bb", "c:/aa/bb/cc", "c:/aa/bb/cc.dd", "c:/aa/bb/cc.dd.ee" }
@@ -3529,6 +3488,14 @@ end
-- test { "/aa", "/aa/bb", "/aa/bb/cc", "/aa/bb/cc.dd", "/aa/bb/cc.dd.ee" }
-- test { "aa", "aa/bb", "aa/bb/cc", "aa/bb/cc.dd", "aa/bb/cc.dd.ee" }
+-- -- maybe:
+--
+-- if os.type == "windows" then
+-- local currentdir = getcurrentdir
+-- function getcurrentdir()
+-- return lpegmatch(reslasher,currentdir())
+-- end
+-- end
-- for myself:
@@ -3537,6 +3504,21 @@ function file.strip(name,dir)
return a ~= "" and a or name
end
+-- local debuglist = {
+-- "pathpart", "basename", "nameonly", "suffixonly", "suffix", "dirname", "extname",
+-- "addsuffix", "removesuffix", "replacesuffix", "join",
+-- "strip","collapsepath", "joinpath", "splitpath",
+-- }
+
+-- for i=1,#debuglist do
+-- local name = debuglist[i]
+-- local f = file[name]
+-- file[name] = function(...)
+-- print(name,f(...))
+-- return f(...)
+-- end
+-- end
+
end -- of closure
@@ -3661,7 +3643,7 @@ if not modules then modules = { } end modules ['l-url'] = {
license = "see context related readme files"
}
-local char, gmatch, gsub, format, byte, find = string.char, string.gmatch, string.gsub, string.format, string.byte, string.find
+local char, format, byte = string.char, string.format, string.byte
local concat = table.concat
local tonumber, type = tonumber, type
local P, C, R, S, Cs, Cc, Ct, Cf, Cg, V = lpeg.P, lpeg.C, lpeg.R, lpeg.S, lpeg.Cs, lpeg.Cc, lpeg.Ct, lpeg.Cf, lpeg.Cg, lpeg.V
@@ -3700,6 +3682,8 @@ local nothing = Cc("")
local escapedchar = (percent * C(hexdigit * hexdigit)) / tochar
local escaped = (plus / " ") + escapedchar
+local noslash = P("/") / ""
+
-- we assume schemes with more than 1 character (in order to avoid problems with windows disks)
-- we also assume that when we have a scheme, we also have an authority
--
@@ -3878,29 +3862,23 @@ function url.construct(hash) -- dodo: we need to escape !
return lpegmatch(escaper,concat(fullurl))
end
-function url.filename(filename) -- why no lpeg here ?
- local t = hashed(filename)
- return (t.scheme == "file" and (gsub(t.path,"^/([a-zA-Z])([:|])/)","%1:"))) or filename
+local pattern = Cs(noslash * R("az","AZ") * (S(":|")/":") * noslash * P(1)^0)
+
+function url.filename(filename)
+ local spec = hashed(filename)
+ local path = spec.path
+ return (spec.scheme == "file" and path and lpegmatch(pattern,path)) or filename
end
+-- print(url.filename("/c|/test"))
+-- print(url.filename("/c/test"))
+
local function escapestring(str)
return lpegmatch(escaper,str)
end
url.escape = escapestring
--- function url.query(str) -- separator could be an option
--- if type(str) == "string" then
--- local t = { }
--- for k, v in gmatch(str,"([^&=]*)=([^&=]*)") do
--- t[k] = v
--- end
--- return t
--- else
--- return str
--- end
--- end
-
function url.query(str)
if type(str) == "string" then
return lpegmatch(splitquery,str) or ""
@@ -3928,14 +3906,19 @@ end
-- /test/ | /test | test/ | test => test
+local pattern = Cs(noslash^0 * (1 - noslash * P(-1))^0)
+
function url.barepath(path)
if not path or path == "" then
return ""
else
- return (gsub(path,"^/?(.-)/?$","%1"))
+ return lpegmatch(pattern,path)
end
end
+-- print(url.barepath("/test"),url.barepath("test/"),url.barepath("/test/"),url.barepath("test"))
+-- print(url.barepath("/x/yz"),url.barepath("x/yz/"),url.barepath("/x/yz/"),url.barepath("x/yz"))
+
@@ -6191,8 +6174,8 @@ end
-- for chem (currently one level)
local value = P(lbrace * C((nobrace + nestedbraces)^0) * rbrace)
- + C(digit^1 * lparent * (noparent + nestedparents)^0 * rparent)
- + C((nestedbraces + (1-comma))^0)
+ + C(digit^1 * lparent * (noparent + nestedparents)^1 * rparent)
+ + C((nestedbraces + (1-comma))^1)
local pattern_a = spaces * Ct(value*(separator*value)^0)
local function repeater(n,str)
@@ -6216,15 +6199,15 @@ local function repeater(n,str)
end
local value = P(lbrace * C((nobrace + nestedbraces)^0) * rbrace)
- + (C(digit^1)/tonumber * lparent * Cs((noparent + nestedparents)^0) * rparent) / repeater
- + C((nestedbraces + (1-comma))^0)
+ + (C(digit^1)/tonumber * lparent * Cs((noparent + nestedparents)^1) * rparent) / repeater
+ + C((nestedbraces + (1-comma))^1)
local pattern_b = spaces * Ct(value*(separator*value)^0)
-function parsers.settings_to_array_with_repeat(str,expand)
+function parsers.settings_to_array_with_repeat(str,expand) -- beware: "" => { }
if expand then
- return lpegmatch(pattern_b,str)
+ return lpegmatch(pattern_b,str) or { }
else
- return lpegmatch(pattern_a,str)
+ return lpegmatch(pattern_a,str) or { }
end
end
diff --git a/scripts/context/stubs/unix/mtxrun b/scripts/context/stubs/unix/mtxrun
index be3acb7da..01c601eb5 100755
--- a/scripts/context/stubs/unix/mtxrun
+++ b/scripts/context/stubs/unix/mtxrun
@@ -1625,7 +1625,8 @@ function lpeg.replacer(one,two,makefunction)
elseif no == 1 then
local o = one[1]
one, two = P(o[1]), o[2]
- pattern = Cs(((1-one)^1 + one/two)^0)
+ -- pattern = Cs(((1-one)^1 + one/two)^0)
+ pattern = Cs((one/two + 1)^0)
else
for i=1,no do
local o = one[i]
@@ -1636,7 +1637,28 @@ function lpeg.replacer(one,two,makefunction)
else
one = P(one)
two = two or ""
- pattern = Cs(((1-one)^1 + one/two)^0)
+ -- pattern = Cs(((1-one)^1 + one/two)^0)
+ pattern = Cs((one/two +1)^0)
+ end
+ if makefunction then
+ return function(str)
+ return lpegmatch(pattern,str)
+ end
+ else
+ return pattern
+ end
+end
+
+function lpeg.finder(lst,makefunction)
+ local pattern
+ if type(lst) == "table" then
+ local p = P(false)
+ for i=1,#lst do
+ p = p + P(lst[i])
+ end
+ pattern = (p + 1)^0
+ else
+ pattern = (P(lst) + 1)^0
end
if makefunction then
return function(str)
@@ -3058,66 +3080,145 @@ file = file or { }
local file = file
local insert, concat = table.insert, table.concat
-local find, gmatch, match, gsub, sub, char, lower = string.find, string.gmatch, string.match, string.gsub, string.sub, string.char, string.lower
+local match = string.match
local lpegmatch = lpeg.match
local getcurrentdir, attributes = lfs.currentdir, lfs.attributes
+local checkedsplit = string.checkedsplit
+
+-- local patterns = file.patterns or { }
+-- file.patterns = patterns
-local P, R, S, C, Cs, Cp, Cc = lpeg.P, lpeg.R, lpeg.S, lpeg.C, lpeg.Cs, lpeg.Cp, lpeg.Cc
+local P, R, S, C, Cs, Cp, Cc, Ct = lpeg.P, lpeg.R, lpeg.S, lpeg.C, lpeg.Cs, lpeg.Cp, lpeg.Cc, lpeg.Ct
-local function dirname(name,default)
- return match(name,"^(.+)[/\\].-$") or (default or "")
+local colon = P(":")
+local period = P(".")
+local periods = P("..")
+local fwslash = P("/")
+local bwslash = P("\\")
+local slashes = S("\\/")
+local noperiod = 1-period
+local noslashes = 1-slashes
+local name = noperiod^1
+local suffix = period/"" * (1-period-slashes)^1 * -1
+
+local pattern = C((noslashes^0 * slashes^1)^1)
+
+local function pathpart(name,default)
+ return lpegmatch(pattern,name) or default or ""
end
+local pattern = (noslashes^0 * slashes)^1 * C(noslashes^1) * -1
+
local function basename(name)
- return match(name,"^.+[/\\](.-)$") or name
+ return lpegmatch(pattern,name) or name
end
--- local function nameonly(name)
--- return (gsub(match(name,"^.+[/\\](.-)$") or name,"%..*$",""))
--- end
+local pattern = (noslashes^0 * slashes^1)^0 * Cs((1-suffix)^1) * suffix^0
local function nameonly(name)
- return (gsub(match(name,"^.+[/\\](.-)$") or name,"%.[%a%d]+$",""))
+ return lpegmatch(pattern,name) or name
end
-local function suffixonly(name,default)
- return match(name,"^.+%.([^/\\]-)$") or default or ""
-end
+local pattern = (noslashes^0 * slashes)^0 * (noperiod^1 * period)^1 * C(noperiod^1) * -1
-local function splitname(name)
- local n, s = match(name,"^(.+)%.([^/\\]-)$")
- return n or name, s or ""
+local function suffixonly(name)
+ return lpegmatch(pattern,name) or ""
end
+file.pathpart = pathpart
file.basename = basename
-
-file.pathpart = dirname
-file.dirname = dirname
-
file.nameonly = nameonly
-
file.suffixonly = suffixonly
-file.extname = suffixonly -- obsolete
file.suffix = suffixonly
-function file.removesuffix(filename)
- return (gsub(filename,"%.[%a%d]+$",""))
+file.dirname = pathpart -- obsolete
+file.extname = suffixonly -- obsolete
+
+-- actually these are schemes
+
+local drive = C(R("az","AZ")) * colon
+local path = C(((1-slashes)^0 * slashes)^0)
+local suffix = period * C(P(1-period)^0 * P(-1))
+local base = C((1-suffix)^0)
+local rest = C(P(1)^0)
+
+drive = drive + Cc("")
+path = path + Cc("")
+base = base + Cc("")
+suffix = suffix + Cc("")
+
+local pattern_a = drive * path * base * suffix
+local pattern_b = path * base * suffix
+local pattern_c = C(drive * path) * C(base * suffix) -- trick: two extra captures
+local pattern_d = path * rest
+
+function file.splitname(str,splitdrive)
+ if splitdrive then
+ return lpegmatch(pattern_a,str) -- returns drive, path, base, suffix
+ else
+ return lpegmatch(pattern_b,str) -- returns path, base, suffix
+ end
+end
+
+function file.splitbase(str)
+ return lpegmatch(pattern_d,str) -- returns path, base+suffix
+end
+
+function file.nametotable(str,splitdrive) -- returns table
+ local path, drive, subpath, name, base, suffix = lpegmatch(pattern_c,str)
+ if splitdrive then
+ return {
+ path = path,
+ drive = drive,
+ subpath = subpath,
+ name = name,
+ base = base,
+ suffix = suffix,
+ }
+ else
+ return {
+ path = path,
+ name = name,
+ base = base,
+ suffix = suffix,
+ }
+ end
+end
+
+local pattern = Cs(((period * noperiod^1 * -1)/"" + 1)^1)
+
+function file.removesuffix(name)
+ return lpegmatch(pattern,name)
end
+-- local pattern = (noslashes^0 * slashes)^0 * (noperiod^1 * period)^1 * Cp() * noperiod^1 * -1
+--
+-- function file.addsuffix(name, suffix)
+-- local p = lpegmatch(pattern,name)
+-- if p then
+-- return name
+-- else
+-- return name .. "." .. suffix
+-- end
+-- end
+
+local suffix = period/"" * (1-period-slashes)^1 * -1
+local pattern = Cs((noslashes^0 * slashes^1)^0 * ((1-suffix)^1)) * Cs(suffix)
+
function file.addsuffix(filename, suffix, criterium)
if not suffix or suffix == "" then
return filename
elseif criterium == true then
return filename .. "." .. suffix
elseif not criterium then
- local n, s = splitname(filename)
+ local n, s = lpegmatch(pattern,filename)
if not s or s == "" then
return filename .. "." .. suffix
else
return filename
end
else
- local n, s = splitname(filename)
+ local n, s = lpegmatch(pattern,filename)
if s and s ~= "" then
local t = type(criterium)
if t == "table" then
@@ -3134,88 +3235,49 @@ function file.addsuffix(filename, suffix, criterium)
end
end
end
- return n .. "." .. suffix
+ return (n or filename) .. "." .. suffix
end
end
+-- print("1 " .. file.addsuffix("name","new") .. " -> name.new")
+-- print("2 " .. file.addsuffix("name.old","new") .. " -> name.old")
+-- print("3 " .. file.addsuffix("name.old","new",true) .. " -> name.old.new")
+-- print("4 " .. file.addsuffix("name.old","new","new") .. " -> name.new")
+-- print("5 " .. file.addsuffix("name.old","new","old") .. " -> name.old")
+-- print("6 " .. file.addsuffix("name.old","new","foo") .. " -> name.new")
+-- print("7 " .. file.addsuffix("name.old","new",{"foo","bar"}) .. " -> name.new")
+-- print("8 " .. file.addsuffix("name.old","new",{"old","bar"}) .. " -> name.old")
-function file.replacesuffix(filename, suffix)
- return (gsub(filename,"%.[%a%d]+$","")) .. "." .. suffix
+local suffix = period * (1-period-slashes)^1 * -1
+local pattern = Cs((1-suffix)^0)
+
+function file.replacesuffix(name,suffix)
+ if suffix and suffix ~= "" then
+ return lpegmatch(pattern,name) .. "." .. suffix
+ else
+ return name
+ end
end
-local trick_1 = char(1)
-local trick_2 = "^" .. trick_1 .. "/+"
+--
-function file.join(...) -- rather dirty
- local lst = { ... }
- local a, b = lst[1], lst[2]
- if not a or a == "" then -- not a added
- lst[1] = trick_1
- elseif b and find(a,"^/+$") and find(b,"^/") then
- lst[1] = ""
- lst[2] = gsub(b,"^/+","")
- end
- local pth = concat(lst,"/")
- pth = gsub(pth,"\\","/")
- local a, b = match(pth,"^(.*://)(.*)$")
- if a and b then
- return a .. gsub(b,"//+","/")
- end
- a, b = match(pth,"^(//)(.*)$")
- if a and b then
- return a .. gsub(b,"//+","/")
- end
- pth = gsub(pth,trick_2,"")
- return (gsub(pth,"//+","/"))
-end
-
--- local slash = P("/")
--- local colon = P(":")
-
--- local replacer = lpeg.replacer(S("\\/")^1,"/")
--- local stripper = Cs(P(slash)^0/"" * replacer)
--- local isnetwork = slash * slash * (1-slash) + (1-slash-colon)^1 * colon
--- local isroot = slash^1 * -1
--- local hasroot = slash^1
-
--- function file.newjoin(...) -- rather dirty
--- local lst = { ... }
--- local one = lst[1]
--- if lpegmatch(isnetwork,one) then
--- local two = lpegmatch(replacer,concat(lst,"/",2))
--- return one .. two
--- elseif lpegmatch(isroot,one) then
--- local two = lpegmatch(replacer,concat(lst,"/",2))
--- if lpegmatch(hasroot,two) then
--- return two
--- else
--- return "/" .. two
--- end
--- elseif one == "" then
--- return lpegmatch(stripper,concat(lst,"/",2))
--- else
--- return lpegmatch(replacer,concat(lst,"/"))
--- end
--- end
+local reslasher = lpeg.replacer(S("\\"),"/")
--- print(file.join("//","/y"))
--- print(file.join("/","/y"))
--- print(file.join("","/y"))
--- print(file.join("/x/","/y"))
--- print(file.join("x/","/y"))
--- print(file.join("http://","/y"))
--- print(file.join("http://a","/y"))
--- print(file.join("http:///a","/y"))
--- print(file.join("//nas-1","/y"))
+function file.reslash(str)
+ return lpegmatch(reslasher,str)
+end
-- We should be able to use:
--
+-- local writable = P(1) * P("w") * Cc(true)
+--
-- function file.is_writable(name)
--- local a = attributes(name) or attributes(dirname(name,"."))
--- return a and sub(a.permissions,2,2) == "w"
+-- local a = attributes(name) or attributes(pathpart(name,"."))
+-- return a and lpegmatch(writable,a.permissions) or false
-- end
--
--- But after some testing Taco and I came up with:
+-- But after some testing Taco and I came up with the more robust
+-- variant:
function file.is_writable(name)
if lfs.isdir(name) then
@@ -3243,9 +3305,11 @@ function file.is_writable(name)
return false
end
+local readable = P("r") * Cc(true)
+
function file.is_readable(name)
local a = attributes(name)
- return a and sub(a.permissions,1,1) == "r"
+ return a and lpegmatch(readable,a.permissions) or false
end
file.isreadable = file.is_readable -- depricated
@@ -3256,41 +3320,74 @@ function file.size(name)
return a and a.size or 0
end
--- todo: lpeg \\ / .. does not save much
-
-local checkedsplit = string.checkedsplit
-
-function file.splitpath(str,separator) -- string
- str = gsub(str,"\\","/")
- return checkedsplit(str,separator or io.pathseparator)
+function file.splitpath(str,separator) -- string .. reslash is a bonus (we could do a direct split)
+ return checkedsplit(lpegmatch(reslasher,str),separator or io.pathseparator)
end
function file.joinpath(tab,separator) -- table
return concat(tab,separator or io.pathseparator) -- can have trailing //
end
--- we can hash them weakly
+local stripper = Cs(P(fwslash)^0/"" * reslasher)
+local isnetwork = fwslash * fwslash * (1-fwslash) + (1-fwslash-colon)^1 * colon
+local isroot = fwslash^1 * -1
+local hasroot = fwslash^1
+
+function file.join(...) -- rather dirty
+ local lst = { ... }
+ local one = lst[1]
+ if lpegmatch(isnetwork,one) then
+ local two = lpegmatch(reslasher,concat(lst,"/",2))
+ return one .. "/" .. two
+ elseif lpegmatch(isroot,one) then
+ local two = lpegmatch(reslasher,concat(lst,"/",2))
+ if lpegmatch(hasroot,two) then
+ return two
+ else
+ return "/" .. two
+ end
+ elseif one == "" then
+ return lpegmatch(stripper,concat(lst,"/",2))
+ else
+ return lpegmatch(reslasher,concat(lst,"/"))
+ end
+end
+
+-- print(file.join("c:/whatever","name"))
+-- print(file.join("//","/y"))
+-- print(file.join("/","/y"))
+-- print(file.join("","/y"))
+-- print(file.join("/x/","/y"))
+-- print(file.join("x/","/y"))
+-- print(file.join("http://","/y"))
+-- print(file.join("http://a","/y"))
+-- print(file.join("http:///a","/y"))
+-- print(file.join("//nas-1","/y"))
+
+-- The previous one fails on "a.b/c" so Taco came up with a split based
+-- variant. After some skyping we got it sort of compatible with the old
+-- one. After that the anchoring to currentdir was added in a better way.
+-- Of course there are some optimizations too. Finally we had to deal with
+-- windows drive prefixes and things like sys://. Eventually gsubs and
+-- finds were replaced by lpegs.
+local drivespec = R("az","AZ")^1 * colon
+local anchors = fwslash + drivespec
+local untouched = periods + (1-period)^1 * P(-1)
+local splitstarter = (Cs(drivespec * (bwslash/"/" + fwslash)^0) + Cc(false)) * Ct(lpeg.splitat(S("/\\")^1))
+local absolute = fwslash
function file.collapsepath(str,anchor)
- if anchor and not find(str,"^/") and not find(str,"^%a:") then
+ if anchor and not lpegmatch(anchors,str) then
str = getcurrentdir() .. "/" .. str
end
if str == "" or str =="." then
return "."
- elseif find(str,"^%.%.") then
- str = gsub(str,"\\","/")
- return str
- elseif not find(str,"%.") then
- str = gsub(str,"\\","/")
- return str
- end
- str = gsub(str,"\\","/")
- local starter, rest = match(str,"^(%a+:/*)(.-)$")
- if starter then
- str = rest
+ elseif lpegmatch(untouched,str) then
+ return lpegmatch(reslasher,str)
end
- local oldelements = checkedsplit(str,"/")
+ local starter, oldelements = lpegmatch(splitstarter,str)
+-- inspect(oldelements)
local newelements = { }
local i = #oldelements
while i > 0 do
@@ -3320,7 +3417,7 @@ function file.collapsepath(str,anchor)
return starter or "."
elseif starter then
return starter .. concat(newelements, '/')
- elseif find(str,"^/") then
+ elseif lpegmatch(absolute,str) then
return "/" .. concat(newelements,'/')
else
return concat(newelements, '/')
@@ -3337,29 +3434,21 @@ end
-- test("a/./b/..") test("a/aa/../b/bb") test("a/.././././b/..") test("a/./././b/..")
-- test("a/b/c/../..") test("./a/b/c/../..") test("a/b/c/../..")
+local validchars = R("az","09","AZ","--","..")
+local pattern_a = lpeg.replacer(1-validchars)
+local pattern_a = Cs((validchars + P(1)/"-")^1)
+local whatever = P("-")^0 / ""
+local pattern_b = Cs(whatever * (1 - whatever * -1)^1)
+
function file.robustname(str,strict)
- str = gsub(str,"[^%a%d%/%-%.\\]+","-")
+ str = lpegmatch(pattern_a,str) or str
if strict then
- return lower(gsub(str,"^%-*(.-)%-*$","%1"))
+ return lpegmatch(pattern_b,str) or str -- two step is cleaner (less backtracking)
else
return str
end
end
--- local pattern_a = lpeg.replacer(1-R("az","09","AZ","--",".."))
--- local pattern_a = Cs((R("az","09","AZ","--","..") + P(1)/"-")^1)
--- local whatever = P("-")^0 / ""
--- local pattern_b = Cs(whatever * (1 - whatever * -1)^1)
-
--- function file.robustname(str,strict)
--- str = lpegmatch(pattern_a,str) or str
--- if strict then
--- return lpegmatch(pattern_b,str) or str -- two step is cleaner (less backtracking)
--- else
--- return str
--- end
--- end
-
file.readdata = io.loaddata
file.savedata = io.savedata
@@ -3367,92 +3456,17 @@ function file.copy(oldname,newname)
file.savedata(newname,io.loaddata(oldname))
end
--- lpeg variants, slightly faster, not always
-
--- local period = P(".")
--- local slashes = S("\\/")
--- local noperiod = 1-period
--- local noslashes = 1-slashes
--- local name = noperiod^1
-
--- local pattern = (noslashes^0 * slashes)^0 * (noperiod^1 * period)^1 * C(noperiod^1) * -1
-
--- function file.suffixonly(name)
--- return lpegmatch(pattern,name) or ""
--- end
-
--- local pattern = Cs(((period * noperiod^1 * -1)/"" + 1)^1)
-
--- function file.removesuffix(name)
--- return lpegmatch(pattern,name)
--- end
-
--- local pattern = (noslashes^0 * slashes)^1 * C(noslashes^1) * -1
-
--- function file.basename(name)
--- return lpegmatch(pattern,name) or name
--- end
-
--- local pattern = Cs ((1 - slashes * noslashes^1 * -1)^1)
-
--- function file.dirname(name)
--- return lpegmatch(pattern,name) or ""
--- end
-
--- local pattern = (noslashes^0 * slashes)^0 * (noperiod^1 * period)^1 * Cp() * noperiod^1 * -1
-
--- function file.addsuffix(name, suffix)
--- local p = lpegmatch(pattern,name)
--- if p then
--- return name
--- else
--- return name .. "." .. suffix
--- end
--- end
-
--- local suffix = period * (1-period-slashes)^1 * -1
--- local pattern = Cs((1-suffix)^1)
-
--- function file.replacesuffix(name,suffix)
--- if suffix and suffix ~= "" then
--- return lpegmatch(pattern,name) .. "." .. suffix
--- else
--- return name
--- end
--- end
-
--- local path = noslashes^0 * slashes^1
--- local suffix = period * (1-period-slashes)^1 * -1
--- local pattern = path^0 * Cs((1-suffix)^1) * suffix^0
-
--- function file.nameonly(name)
--- return lpegmatch(pattern,name) or name
--- end
-
--- local test = file.suffixonly
--- local test = file.basename
--- local test = file.dirname
--- local test = file.addsuffix
--- local test = file.replacesuffix
--- local test = file.nameonly
-
--- print(1,test("./a/b/c/abd.def.xxx","!!!"))
--- print(2,test("./../b/c/abd.def.xxx","!!!"))
--- print(3,test("a/b/c/abd.def.xxx","!!!"))
--- print(4,test("a/b/c/def.xxx","!!!"))
--- print(5,test("a/b/c/def","!!!"))
--- print(6,test("def","!!!"))
--- print(7,test("def.xxx","!!!"))
-
--- local tim = os.clock() for i=1,250000 do local ext = test("abd.def.xxx","!!!") end print(os.clock()-tim)
-
-- also rewrite previous
local letter = R("az","AZ") + S("_-+")
local separator = P("://")
-local qualified = P(".")^0 * P("/") + letter*P(":") + letter^1*separator + letter^1 * P("/")
-local rootbased = P("/") + letter*P(":")
+local qualified = period^0 * fwslash
+ + letter * colon
+ + letter^1 * separator
+ + letter^1 * fwslash
+local rootbased = fwslash
+ + letter * colon
lpeg.patterns.qualified = qualified
lpeg.patterns.rootbased = rootbased
@@ -3467,61 +3481,6 @@ function file.is_rootbased_path(filename)
return lpegmatch(rootbased,filename) ~= nil
end
--- actually these are schemes
-
-local slash = S("\\/")
-local period = P(".")
-local drive = C(R("az","AZ")) * P(":")
-local path = C(((1-slash)^0 * slash)^0)
-local suffix = period * C(P(1-period)^0 * P(-1))
-local base = C((1-suffix)^0)
-local rest = C(P(1)^0)
-
-drive = drive + Cc("")
-path = path + Cc("")
-base = base + Cc("")
-suffix = suffix + Cc("")
-
-local pattern_a = drive * path * base * suffix
-local pattern_b = path * base * suffix
-local pattern_c = C(drive * path) * C(base * suffix) -- trick: two extra captures
-local pattern_d = path * rest
-
-function file.splitname(str,splitdrive)
- if splitdrive then
- return lpegmatch(pattern_a,str) -- returns drive, path, base, suffix
- else
- return lpegmatch(pattern_b,str) -- returns path, base, suffix
- end
-end
-
-function file.splitbase(str)
- return lpegmatch(pattern_d,str) -- returns path, base+suffix
-end
-
-function file.nametotable(str,splitdrive) -- returns table
- local path, drive, subpath, name, base, suffix = lpegmatch(pattern_c,str)
- if splitdrive then
- return {
- path = path,
- drive = drive,
- subpath = subpath,
- name = name,
- base = base,
- suffix = suffix,
- }
- else
- return {
- path = path,
- name = name,
- base = base,
- suffix = suffix,
- }
- end
-end
-
--- print(file.splitbase("a/b/c.txt"))
-
-- function test(t) for k, v in next, t do print(v, "=>", file.splitname(v)) end end
--
-- test { "c:", "c:/aa", "c:/aa/bb", "c:/aa/bb/cc", "c:/aa/bb/cc.dd", "c:/aa/bb/cc.dd.ee" }
@@ -3529,6 +3488,14 @@ end
-- test { "/aa", "/aa/bb", "/aa/bb/cc", "/aa/bb/cc.dd", "/aa/bb/cc.dd.ee" }
-- test { "aa", "aa/bb", "aa/bb/cc", "aa/bb/cc.dd", "aa/bb/cc.dd.ee" }
+-- -- maybe:
+--
+-- if os.type == "windows" then
+-- local currentdir = getcurrentdir
+-- function getcurrentdir()
+-- return lpegmatch(reslasher,currentdir())
+-- end
+-- end
-- for myself:
@@ -3537,6 +3504,21 @@ function file.strip(name,dir)
return a ~= "" and a or name
end
+-- local debuglist = {
+-- "pathpart", "basename", "nameonly", "suffixonly", "suffix", "dirname", "extname",
+-- "addsuffix", "removesuffix", "replacesuffix", "join",
+-- "strip","collapsepath", "joinpath", "splitpath",
+-- }
+
+-- for i=1,#debuglist do
+-- local name = debuglist[i]
+-- local f = file[name]
+-- file[name] = function(...)
+-- print(name,f(...))
+-- return f(...)
+-- end
+-- end
+
end -- of closure
@@ -3661,7 +3643,7 @@ if not modules then modules = { } end modules ['l-url'] = {
license = "see context related readme files"
}
-local char, gmatch, gsub, format, byte, find = string.char, string.gmatch, string.gsub, string.format, string.byte, string.find
+local char, format, byte = string.char, string.format, string.byte
local concat = table.concat
local tonumber, type = tonumber, type
local P, C, R, S, Cs, Cc, Ct, Cf, Cg, V = lpeg.P, lpeg.C, lpeg.R, lpeg.S, lpeg.Cs, lpeg.Cc, lpeg.Ct, lpeg.Cf, lpeg.Cg, lpeg.V
@@ -3700,6 +3682,8 @@ local nothing = Cc("")
local escapedchar = (percent * C(hexdigit * hexdigit)) / tochar
local escaped = (plus / " ") + escapedchar
+local noslash = P("/") / ""
+
-- we assume schemes with more than 1 character (in order to avoid problems with windows disks)
-- we also assume that when we have a scheme, we also have an authority
--
@@ -3878,29 +3862,23 @@ function url.construct(hash) -- dodo: we need to escape !
return lpegmatch(escaper,concat(fullurl))
end
-function url.filename(filename) -- why no lpeg here ?
- local t = hashed(filename)
- return (t.scheme == "file" and (gsub(t.path,"^/([a-zA-Z])([:|])/)","%1:"))) or filename
+local pattern = Cs(noslash * R("az","AZ") * (S(":|")/":") * noslash * P(1)^0)
+
+function url.filename(filename)
+ local spec = hashed(filename)
+ local path = spec.path
+ return (spec.scheme == "file" and path and lpegmatch(pattern,path)) or filename
end
+-- print(url.filename("/c|/test"))
+-- print(url.filename("/c/test"))
+
local function escapestring(str)
return lpegmatch(escaper,str)
end
url.escape = escapestring
--- function url.query(str) -- separator could be an option
--- if type(str) == "string" then
--- local t = { }
--- for k, v in gmatch(str,"([^&=]*)=([^&=]*)") do
--- t[k] = v
--- end
--- return t
--- else
--- return str
--- end
--- end
-
function url.query(str)
if type(str) == "string" then
return lpegmatch(splitquery,str) or ""
@@ -3928,14 +3906,19 @@ end
-- /test/ | /test | test/ | test => test
+local pattern = Cs(noslash^0 * (1 - noslash * P(-1))^0)
+
function url.barepath(path)
if not path or path == "" then
return ""
else
- return (gsub(path,"^/?(.-)/?$","%1"))
+ return lpegmatch(pattern,path)
end
end
+-- print(url.barepath("/test"),url.barepath("test/"),url.barepath("/test/"),url.barepath("test"))
+-- print(url.barepath("/x/yz"),url.barepath("x/yz/"),url.barepath("/x/yz/"),url.barepath("x/yz"))
+
@@ -6191,8 +6174,8 @@ end
-- for chem (currently one level)
local value = P(lbrace * C((nobrace + nestedbraces)^0) * rbrace)
- + C(digit^1 * lparent * (noparent + nestedparents)^0 * rparent)
- + C((nestedbraces + (1-comma))^0)
+ + C(digit^1 * lparent * (noparent + nestedparents)^1 * rparent)
+ + C((nestedbraces + (1-comma))^1)
local pattern_a = spaces * Ct(value*(separator*value)^0)
local function repeater(n,str)
@@ -6216,15 +6199,15 @@ local function repeater(n,str)
end
local value = P(lbrace * C((nobrace + nestedbraces)^0) * rbrace)
- + (C(digit^1)/tonumber * lparent * Cs((noparent + nestedparents)^0) * rparent) / repeater
- + C((nestedbraces + (1-comma))^0)
+ + (C(digit^1)/tonumber * lparent * Cs((noparent + nestedparents)^1) * rparent) / repeater
+ + C((nestedbraces + (1-comma))^1)
local pattern_b = spaces * Ct(value*(separator*value)^0)
-function parsers.settings_to_array_with_repeat(str,expand)
+function parsers.settings_to_array_with_repeat(str,expand) -- beware: "" => { }
if expand then
- return lpegmatch(pattern_b,str)
+ return lpegmatch(pattern_b,str) or { }
else
- return lpegmatch(pattern_a,str)
+ return lpegmatch(pattern_a,str) or { }
end
end