You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
748 lines
22 KiB
748 lines
22 KiB
if not modules then modules = { } end modules ['l-file'] = {
|
|
version = 1.001,
|
|
comment = "companion to luat-lib.mkiv",
|
|
author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
|
|
copyright = "PRAGMA ADE / ConTeXt Development Team",
|
|
license = "see context related readme files"
|
|
}
|
|
|
|
-- needs a cleanup
|
|
|
|
file = file or { }
|
|
local file = file
|
|
|
|
if not lfs then
|
|
lfs = optionalrequire("lfs")
|
|
end
|
|
|
|
-- -- see later
|
|
--
|
|
-- if not lfs then
|
|
--
|
|
-- lfs = {
|
|
-- getcurrentdir = function()
|
|
-- return "."
|
|
-- end,
|
|
-- attributes = function()
|
|
-- return nil
|
|
-- end,
|
|
-- isfile = function(name)
|
|
-- local f = io.open(name,'rb')
|
|
-- if f then
|
|
-- f:close()
|
|
-- return true
|
|
-- end
|
|
-- end,
|
|
-- isdir = function(name)
|
|
-- print("you need to load lfs")
|
|
-- return false
|
|
-- end
|
|
-- }
|
|
--
|
|
-- elseif not lfs.isfile then
|
|
--
|
|
-- local attributes = lfs.attributes
|
|
--
|
|
-- function lfs.isdir(name)
|
|
-- return attributes(name,"mode") == "directory"
|
|
-- end
|
|
--
|
|
-- function lfs.isfile(name)
|
|
-- return attributes(name,"mode") == "file"
|
|
-- end
|
|
--
|
|
-- -- function lfs.isdir(name)
|
|
-- -- local a = attributes(name)
|
|
-- -- return a and a.mode == "directory"
|
|
-- -- end
|
|
--
|
|
-- -- function lfs.isfile(name)
|
|
-- -- local a = attributes(name)
|
|
-- -- return a and a.mode == "file"
|
|
-- -- end
|
|
--
|
|
-- end
|
|
|
|
local insert, concat = table.insert, table.concat
|
|
local match, find, gmatch = string.match, string.find, string.gmatch
|
|
local lpegmatch = lpeg.match
|
|
local getcurrentdir, attributes = lfs.currentdir, lfs.attributes
|
|
local checkedsplit = string.checkedsplit
|
|
|
|
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
|
|
|
|
-- better this way:
|
|
|
|
local attributes = lfs.attributes
|
|
|
|
function lfs.isdir(name)
|
|
if name then
|
|
return attributes(name,"mode") == "directory"
|
|
end
|
|
end
|
|
|
|
function lfs.isfile(name)
|
|
if name then
|
|
local a = attributes(name,"mode")
|
|
return a == "file" or a == "link" or nil
|
|
end
|
|
end
|
|
|
|
function lfs.isfound(name)
|
|
if name then
|
|
local a = attributes(name,"mode")
|
|
return (a == "file" or a == "link") and name or nil
|
|
end
|
|
end
|
|
|
|
function lfs.modification(name)
|
|
return name and attributes(name,"modification") or nil
|
|
end
|
|
|
|
if sandbox then
|
|
sandbox.redefine(lfs.isfile,"lfs.isfile")
|
|
sandbox.redefine(lfs.isdir, "lfs.isdir")
|
|
sandbox.redefine(lfs.isfound, "lfs.isfound")
|
|
end
|
|
|
|
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
|
|
|
|
----- pattern = C((noslashes^0 * slashes^1)^1)
|
|
local pattern = C((1 - (slashes^1 * noslashes^1 * -1))^1) * P(1) -- there must be a more efficient way
|
|
|
|
local function pathpart(name,default)
|
|
return name and lpegmatch(pattern,name) or default or ""
|
|
end
|
|
|
|
local pattern = (noslashes^0 * slashes)^1 * C(noslashes^1) * -1
|
|
|
|
local function basename(name)
|
|
return name and lpegmatch(pattern,name) or name
|
|
end
|
|
|
|
-- print(pathpart("file"))
|
|
-- print(pathpart("dir/file"))
|
|
-- print(pathpart("/dir/file"))
|
|
-- print(basename("file"))
|
|
-- print(basename("dir/file"))
|
|
-- print(basename("/dir/file"))
|
|
|
|
local pattern = (noslashes^0 * slashes^1)^0 * Cs((1-suffix)^1) * suffix^0
|
|
|
|
local function nameonly(name)
|
|
return name and lpegmatch(pattern,name) or name
|
|
end
|
|
|
|
local pattern = (noslashes^0 * slashes)^0 * (noperiod^1 * period)^1 * C(noperiod^1) * -1
|
|
|
|
local function suffixonly(name)
|
|
return name and lpegmatch(pattern,name) or ""
|
|
end
|
|
|
|
local pattern = (noslashes^0 * slashes)^0 * noperiod^1 * ((period * C(noperiod^1))^1) * -1 + Cc("")
|
|
|
|
local function suffixesonly(name)
|
|
if name then
|
|
return lpegmatch(pattern,name)
|
|
else
|
|
return ""
|
|
end
|
|
end
|
|
|
|
file.pathpart = pathpart
|
|
file.basename = basename
|
|
file.nameonly = nameonly
|
|
file.suffixonly = suffixonly
|
|
file.suffix = suffixonly
|
|
file.suffixesonly = suffixesonly
|
|
file.suffixes = suffixesonly
|
|
|
|
file.dirname = pathpart -- obsolete
|
|
file.extname = suffixonly -- obsolete
|
|
|
|
-- actually these are schemes
|
|
|
|
local drive = C(R("az","AZ")) * colon
|
|
local path = C((noslashes^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 not str then
|
|
-- error
|
|
elseif 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)
|
|
if str then
|
|
return lpegmatch(pattern_d,str) -- returns path, base+suffix (path has / appended, might change at some point)
|
|
else
|
|
return "", str -- assume no path
|
|
end
|
|
end
|
|
|
|
---- stripslash = C((1 - P("/")^1*P(-1))^0)
|
|
|
|
function file.nametotable(str,splitdrive)
|
|
if str then
|
|
local path, drive, subpath, name, base, suffix = lpegmatch(pattern_c,str)
|
|
-- if path ~= "" then
|
|
-- path = lpegmatch(stripslash,path) -- unfortunate hack, maybe this becomes default
|
|
-- end
|
|
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
|
|
end
|
|
|
|
-- print(file.splitname("file"))
|
|
-- print(file.splitname("dir/file"))
|
|
-- print(file.splitname("/dir/file"))
|
|
-- print(file.splitname("file"))
|
|
-- print(file.splitname("dir/file"))
|
|
-- print(file.splitname("/dir/file"))
|
|
|
|
-- inspect(file.nametotable("file.ext"))
|
|
-- inspect(file.nametotable("dir/file.ext"))
|
|
-- inspect(file.nametotable("/dir/file.ext"))
|
|
-- inspect(file.nametotable("file.ext"))
|
|
-- inspect(file.nametotable("dir/file.ext"))
|
|
-- inspect(file.nametotable("/dir/file.ext"))
|
|
|
|
----- pattern = Cs(((period * noperiod^1 * -1) / "" + 1)^1)
|
|
local pattern = Cs(((period * (1-period-slashes)^1 * -1) / "" + 1)^1)
|
|
|
|
function file.removesuffix(name)
|
|
return name and 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 filename or not suffix or suffix == "" then
|
|
return filename
|
|
elseif criterium == true then
|
|
return filename .. "." .. suffix
|
|
elseif not criterium then
|
|
local n, s = lpegmatch(pattern,filename)
|
|
if not s or s == "" then
|
|
return filename .. "." .. suffix
|
|
else
|
|
return filename
|
|
end
|
|
else
|
|
local n, s = lpegmatch(pattern,filename)
|
|
if s and s ~= "" then
|
|
local t = type(criterium)
|
|
if t == "table" then
|
|
-- keep if in criterium
|
|
for i=1,#criterium do
|
|
if s == criterium[i] then
|
|
return filename
|
|
end
|
|
end
|
|
elseif t == "string" then
|
|
-- keep if criterium
|
|
if s == criterium then
|
|
return filename
|
|
end
|
|
end
|
|
end
|
|
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")
|
|
|
|
local suffix = period * (1-period-slashes)^1 * -1
|
|
local pattern = Cs((1-suffix)^0)
|
|
|
|
function file.replacesuffix(name,suffix)
|
|
if name and suffix and suffix ~= "" then
|
|
return lpegmatch(pattern,name) .. "." .. suffix
|
|
else
|
|
return name
|
|
end
|
|
end
|
|
|
|
--
|
|
|
|
local reslasher = lpeg.replacer(P("\\"),"/")
|
|
|
|
function file.reslash(str)
|
|
return str and 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(pathpart(name,"."))
|
|
-- return a and lpegmatch(writable,a.permissions) or false
|
|
-- end
|
|
--
|
|
-- But after some testing Taco and I came up with the more robust
|
|
-- variant:
|
|
|
|
if lfs.isreadablefile and lfs.iswritablefile then
|
|
|
|
file.is_readable = lfs.isreadablefile
|
|
file.is_writable = lfs.iswritablefile
|
|
|
|
else
|
|
|
|
function file.is_writable(name)
|
|
if not name then
|
|
-- error
|
|
elseif lfs.isdir(name) then
|
|
name = name .. "/m_t_x_t_e_s_t.tmp"
|
|
local f = io.open(name,"wb")
|
|
if f then
|
|
f:close()
|
|
os.remove(name)
|
|
return true
|
|
end
|
|
elseif lfs.isfile(name) then
|
|
local f = io.open(name,"ab")
|
|
if f then
|
|
f:close()
|
|
return true
|
|
end
|
|
else
|
|
local f = io.open(name,"ab")
|
|
if f then
|
|
f:close()
|
|
os.remove(name)
|
|
return true
|
|
end
|
|
end
|
|
return false
|
|
end
|
|
|
|
local readable = P("r") * Cc(true)
|
|
|
|
function file.is_readable(name)
|
|
if name then
|
|
local a = attributes(name)
|
|
return a and lpegmatch(readable,a.permissions) or false
|
|
else
|
|
return false
|
|
end
|
|
end
|
|
|
|
end
|
|
|
|
file.isreadable = file.is_readable -- depricated
|
|
file.iswritable = file.is_writable -- depricated
|
|
|
|
function file.size(name)
|
|
if name then
|
|
local a = attributes(name)
|
|
return a and a.size or 0
|
|
else
|
|
return 0
|
|
end
|
|
end
|
|
|
|
function file.splitpath(str,separator) -- string .. reslash is a bonus (we could do a direct split)
|
|
return str and checkedsplit(lpegmatch(reslasher,str),separator or io.pathseparator)
|
|
end
|
|
|
|
function file.joinpath(tab,separator) -- table
|
|
return tab and concat(tab,separator or io.pathseparator) -- can have trailing //
|
|
end
|
|
|
|
local someslash = S("\\/")
|
|
local stripper = Cs(P(fwslash)^0/"" * reslasher)
|
|
local isnetwork = someslash * someslash * (1-someslash)
|
|
+ (1-fwslash-colon)^1 * colon
|
|
local isroot = fwslash^1 * -1
|
|
local hasroot = fwslash^1
|
|
|
|
local reslasher = lpeg.replacer(S("\\/"),"/")
|
|
local deslasher = lpeg.replacer(S("\\/")^1,"/")
|
|
|
|
-- If we have a network or prefix then there is a change that we end up with two
|
|
-- // in the middle ... we could prevent this if we (1) expand prefixes: and (2)
|
|
-- split and rebuild as url. Of course we could assume no network paths (which
|
|
-- makes sense) adn assume either mapped drives (windows) or mounts (unix) but
|
|
-- then we still have to deal with urls ... anyhow, multiple // are never a real
|
|
-- problem but just ugly.
|
|
|
|
-- function file.join(...)
|
|
-- local lst = { ... }
|
|
-- local one = lst[1]
|
|
-- if lpegmatch(isnetwork,one) then
|
|
-- local one = lpegmatch(reslasher,one)
|
|
-- local two = lpegmatch(deslasher,concat(lst,"/",2))
|
|
-- if lpegmatch(hasroot,two) then
|
|
-- return one .. two
|
|
-- else
|
|
-- return one .. "/" .. two
|
|
-- end
|
|
-- elseif lpegmatch(isroot,one) then
|
|
-- local two = lpegmatch(deslasher,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(deslasher,concat(lst,"/"))
|
|
-- end
|
|
-- end
|
|
|
|
function file.join(one, two, three, ...)
|
|
if not two then
|
|
return one == "" and one or lpegmatch(reslasher,one)
|
|
end
|
|
if one == "" then
|
|
return lpegmatch(stripper,three and concat({ two, three, ... },"/") or two)
|
|
end
|
|
if lpegmatch(isnetwork,one) then
|
|
local one = lpegmatch(reslasher,one)
|
|
local two = lpegmatch(deslasher,three and concat({ two, three, ... },"/") or two)
|
|
if lpegmatch(hasroot,two) then
|
|
return one .. two
|
|
else
|
|
return one .. "/" .. two
|
|
end
|
|
elseif lpegmatch(isroot,one) then
|
|
local two = lpegmatch(deslasher,three and concat({ two, three, ... },"/") or two)
|
|
if lpegmatch(hasroot,two) then
|
|
return two
|
|
else
|
|
return "/" .. two
|
|
end
|
|
else
|
|
return lpegmatch(deslasher,concat({ one, two, three, ... },"/"))
|
|
end
|
|
end
|
|
|
|
-- or we can use this:
|
|
--
|
|
-- function file.join(...)
|
|
-- local n = select("#",...)
|
|
-- local one = select(1,...)
|
|
-- if n == 1 then
|
|
-- return one == "" and one or lpegmatch(stripper,one)
|
|
-- end
|
|
-- if one == "" then
|
|
-- return lpegmatch(stripper,n > 2 and concat({ ... },"/",2) or select(2,...))
|
|
-- end
|
|
-- if lpegmatch(isnetwork,one) then
|
|
-- local one = lpegmatch(reslasher,one)
|
|
-- local two = lpegmatch(deslasher,n > 2 and concat({ ... },"/",2) or select(2,...))
|
|
-- if lpegmatch(hasroot,two) then
|
|
-- return one .. two
|
|
-- else
|
|
-- return one .. "/" .. two
|
|
-- end
|
|
-- elseif lpegmatch(isroot,one) then
|
|
-- local two = lpegmatch(deslasher,n > 2 and concat({ ... },"/",2) or select(2,...))
|
|
-- if lpegmatch(hasroot,two) then
|
|
-- return two
|
|
-- else
|
|
-- return "/" .. two
|
|
-- end
|
|
-- else
|
|
-- return lpegmatch(deslasher,concat({ ... },"/"))
|
|
-- end
|
|
-- end
|
|
|
|
-- print(file.join("c:/whatever"))
|
|
-- 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"))
|
|
-- print(file.join("//nas-1/a/b/c","/y"))
|
|
-- print(file.join("\\\\nas-1\\a\\b\\c","\\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 mswindrive = Cs(drivespec * (bwslash/"/" + fwslash)^0)
|
|
local mswinuncpath = (bwslash + fwslash) * (bwslash + fwslash) * Cc("//")
|
|
local splitstarter = (mswindrive + mswinuncpath + Cc(false))
|
|
* Ct(lpeg.splitat(S("/\\")^1))
|
|
local absolute = fwslash
|
|
|
|
function file.collapsepath(str,anchor) -- anchor: false|nil, true, "."
|
|
if not str then
|
|
return
|
|
end
|
|
if anchor == true and not lpegmatch(anchors,str) then
|
|
str = getcurrentdir() .. "/" .. str
|
|
end
|
|
if str == "" or str =="." then
|
|
return "."
|
|
elseif lpegmatch(untouched,str) then
|
|
return lpegmatch(reslasher,str)
|
|
end
|
|
local starter, oldelements = lpegmatch(splitstarter,str)
|
|
local newelements = { }
|
|
local i = #oldelements
|
|
while i > 0 do
|
|
local element = oldelements[i]
|
|
if element == '.' then
|
|
-- do nothing
|
|
elseif element == '..' then
|
|
local n = i - 1
|
|
while n > 0 do
|
|
local element = oldelements[n]
|
|
if element ~= '..' and element ~= '.' then
|
|
oldelements[n] = '.'
|
|
break
|
|
else
|
|
n = n - 1
|
|
end
|
|
end
|
|
if n < 1 then
|
|
insert(newelements,1,'..')
|
|
end
|
|
elseif element ~= "" then
|
|
insert(newelements,1,element)
|
|
end
|
|
i = i - 1
|
|
end
|
|
if #newelements == 0 then
|
|
return starter or "."
|
|
elseif starter then
|
|
return starter .. concat(newelements, '/')
|
|
elseif lpegmatch(absolute,str) then
|
|
return "/" .. concat(newelements,'/')
|
|
else
|
|
newelements = concat(newelements, '/')
|
|
if anchor == "." and find(str,"^%./") then
|
|
return "./" .. newelements
|
|
else
|
|
return newelements
|
|
end
|
|
end
|
|
end
|
|
|
|
-- local function test(str,...)
|
|
-- print(string.format("%-20s %-15s %-30s %-20s",str,file.collapsepath(str),file.collapsepath(str,true),file.collapsepath(str,".")))
|
|
-- end
|
|
-- test("a/b.c/d") test("b.c/d") test("b.c/..")
|
|
-- test("/") test("c:/..") test("sys://..")
|
|
-- test("") test("./") test(".") test("..") test("./..") test("../..")
|
|
-- test("a") test("./a") test("/a") test("a/../..")
|
|
-- 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/../..")
|
|
-- test("./a")
|
|
-- test([[\\a.b.c\d\e]])
|
|
|
|
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)
|
|
if str then
|
|
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
|
|
end
|
|
|
|
local loaddata = io.loaddata
|
|
local savedata = io.savedata
|
|
|
|
file.readdata = loaddata
|
|
file.savedata = savedata
|
|
|
|
function file.copy(oldname,newname)
|
|
if oldname and newname then
|
|
local data = loaddata(oldname)
|
|
if data and data ~= "" then
|
|
savedata(newname,data)
|
|
end
|
|
end
|
|
end
|
|
|
|
-- also rewrite previous
|
|
|
|
local letter = R("az","AZ") + S("_-+")
|
|
local separator = 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
|
|
|
|
-- ./name ../name /name c: :// name/name
|
|
|
|
function file.is_qualified_path(filename)
|
|
return filename and lpegmatch(qualified,filename) ~= nil
|
|
end
|
|
|
|
function file.is_rootbased_path(filename)
|
|
return filename and lpegmatch(rootbased,filename) ~= nil
|
|
end
|
|
|
|
-- 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" }
|
|
-- test { "c:", "c:aa", "c:aa/bb", "c:aa/bb/cc", "c:aa/bb/cc.dd", "c:aa/bb/cc.dd.ee" }
|
|
-- 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:
|
|
|
|
function file.strip(name,dir)
|
|
if name then
|
|
local b, a = match(name,"^(.-)" .. dir .. "(.*)$")
|
|
return a ~= "" and a or name
|
|
end
|
|
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
|
|
|
|
-- a goodie: a dumb version of mkdirs (not used in context itself, only
|
|
-- in generic usage)
|
|
|
|
function lfs.mkdirs(path)
|
|
local full = ""
|
|
for sub in gmatch(path,"(/*[^\\/]+)") do -- accepts leading c: and /
|
|
full = full .. sub
|
|
-- lfs.isdir("/foo") mistakenly returns true on windows so
|
|
-- so we don't test and just make as that one is not too picky
|
|
lfs.mkdir(full)
|
|
end
|
|
end
|
|
|
|
-- here is oen i ran into when messign around with xavante code (keppler project)
|
|
-- where it's called in_base .. no gain in using lpeg here
|
|
|
|
function file.withinbase(path) -- don't go beyond root
|
|
local l = 0
|
|
if not find(path,"^/") then
|
|
path = "/" .. path
|
|
end
|
|
for dir in gmatch(path,"/([^/]+)") do
|
|
if dir == ".." then
|
|
l = l - 1
|
|
elseif dir ~= "." then
|
|
l = l + 1
|
|
end
|
|
if l < 0 then
|
|
return false
|
|
end
|
|
end
|
|
return true
|
|
end
|
|
|
|
-- not used in context but was in luatex once:
|
|
|
|
local symlinkattributes = lfs.symlinkattributes
|
|
|
|
function lfs.readlink(name)
|
|
return symlinkattributes(name,"target") or nil
|
|
end
|
|
|