diff options
author | Hans Hagen <pragma@wxs.nl> | 2012-12-10 23:20:00 +0100 |
---|---|---|
committer | Hans Hagen <pragma@wxs.nl> | 2012-12-10 23:20:00 +0100 |
commit | d2d2f5a76a2323d1adafe3dc1926e95064b206d9 (patch) | |
tree | 03582051cf1ffb8460a9f143f875e893fb849512 /scripts | |
parent | 898996a6e7727a0530533135ce19570647c924fb (diff) | |
download | context-d2d2f5a76a2323d1adafe3dc1926e95064b206d9.tar.gz |
beta 2012.12.10 23:20
Diffstat (limited to 'scripts')
-rw-r--r-- | scripts/context/lua/mtxrun.lua | 571 | ||||
-rw-r--r-- | scripts/context/stubs/mswin/mtxrun.lua | 571 | ||||
-rwxr-xr-x | scripts/context/stubs/unix/mtxrun | 571 |
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 |