diff options
| -rw-r--r-- | otfl-lib-dir.lua | 449 | 
1 files changed, 449 insertions, 0 deletions
| diff --git a/otfl-lib-dir.lua b/otfl-lib-dir.lua new file mode 100644 index 0000000..00cda38 --- /dev/null +++ b/otfl-lib-dir.lua @@ -0,0 +1,449 @@ +if not modules then modules = { } end modules ['l-dir'] = { +    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" +} + +-- dir.expandname will be merged with cleanpath and collapsepath + +local type, select = type, select +local find, gmatch, match, gsub = string.find, string.gmatch, string.match, string.gsub +local concat, insert, remove = table.concat, table.insert, table.remove +local lpegmatch = lpeg.match + +local P, S, R, C, Cc, Cs, Ct, Cv, V = lpeg.P, lpeg.S, lpeg.R, lpeg.C, lpeg.Cc, lpeg.Cs, lpeg.Ct, lpeg.Cv, lpeg.V + +dir = dir or { } +local dir = dir +local lfs = lfs + +local attributes = lfs.attributes +local walkdir    = lfs.dir +local isdir      = lfs.isdir +local isfile     = lfs.isfile +local currentdir = lfs.currentdir +local chdir      = lfs.chdir + +-- in case we load outside luatex + +if not isdir then +    function isdir(name) +        local a = attributes(name) +        return a and a.mode == "directory" +    end +    lfs.isdir = isdir +end + +if not isfile then +    function isfile(name) +        local a = attributes(name) +        return a and a.mode == "file" +    end +    lfs.isfile = isfile +end + +-- handy + +function dir.current() +    return (gsub(currentdir(),"\\","/")) +end + +-- optimizing for no find (*) does not save time + +--~ local function globpattern(path,patt,recurse,action) -- fails in recent luatex due to some change in lfs +--~     local ok, scanner +--~     if path == "/" then +--~         ok, scanner = xpcall(function() return walkdir(path..".") end, function() end) -- kepler safe +--~     else +--~         ok, scanner = xpcall(function() return walkdir(path)      end, function() end) -- kepler safe +--~     end +--~     if ok and type(scanner) == "function" then +--~         if not find(path,"/$") then path = path .. '/' end +--~         for name in scanner do +--~             local full = path .. name +--~             local mode = attributes(full,'mode') +--~             if mode == 'file' then +--~                 if find(full,patt) then +--~                     action(full) +--~                 end +--~             elseif recurse and (mode == "directory") and (name ~= '.') and (name ~= "..") then +--~                 globpattern(full,patt,recurse,action) +--~             end +--~         end +--~     end +--~ end + +local lfsisdir = isdir + +local function isdir(path) +    path = gsub(path,"[/\\]+$","") +    return lfsisdir(path) +end + +lfs.isdir = isdir + +local function globpattern(path,patt,recurse,action) +    if path == "/" then +        path = path .. "." +    elseif not find(path,"/$") then +        path = path .. '/' +    end +    if isdir(path) then -- lfs.isdir does not like trailing / +        for name in walkdir(path) do -- lfs.dir accepts trailing / +            local full = path .. name +            local mode = attributes(full,'mode') +            if mode == 'file' then +                if find(full,patt) then +                    action(full) +                end +            elseif recurse and (mode == "directory") and (name ~= '.') and (name ~= "..") then +                globpattern(full,patt,recurse,action) +            end +        end +    end +end + +dir.globpattern = globpattern + +local function collectpattern(path,patt,recurse,result) +    local ok, scanner +    result = result or { } +    if path == "/" then +        ok, scanner, first = xpcall(function() return walkdir(path..".") end, function() end) -- kepler safe +    else +        ok, scanner, first = xpcall(function() return walkdir(path)      end, function() end) -- kepler safe +    end +    if ok and type(scanner) == "function" then +        if not find(path,"/$") then path = path .. '/' end +        for name in scanner, first do +            local full = path .. name +            local attr = attributes(full) +            local mode = attr.mode +            if mode == 'file' then +                if find(full,patt) then +                    result[name] = attr +                end +            elseif recurse and (mode == "directory") and (name ~= '.') and (name ~= "..") then +                attr.list = collectpattern(full,patt,recurse) +                result[name] = attr +            end +        end +    end +    return result +end + +dir.collectpattern = collectpattern + +local pattern = Ct { +    [1] = (C(P(".") + P("/")^1) + C(R("az","AZ") * P(":") * P("/")^0) + Cc("./")) * V(2) * V(3), +    [2] = C(((1-S("*?/"))^0 * P("/"))^0), +    [3] = C(P(1)^0) +} + +local filter = Cs ( ( +    P("**") / ".*" + +    P("*")  / "[^/]*" + +    P("?")  / "[^/]" + +    P(".")  / "%%." + +    P("+")  / "%%+" + +    P("-")  / "%%-" + +    P(1) +)^0 ) + +local function glob(str,t) +    if type(t) == "function" then +        if type(str) == "table" then +            for s=1,#str do +                glob(str[s],t) +            end +        elseif isfile(str) then +            t(str) +        else +            local split = lpegmatch(pattern,str) -- we could use the file splitter +            if split then +                local root, path, base = split[1], split[2], split[3] +                local recurse = find(base,"%*%*") +                local start = root .. path +                local result = lpegmatch(filter,start .. base) +                globpattern(start,result,recurse,t) +            end +        end +    else +        if type(str) == "table" then +            local t = t or { } +            for s=1,#str do +                glob(str[s],t) +            end +            return t +        elseif isfile(str) then +            if t then +                t[#t+1] = str +                return t +            else +                return { str } +            end +        else +            local split = lpegmatch(pattern,str) -- we could use the file splitter +            if split then +                local t = t or { } +                local action = action or function(name) t[#t+1] = name end +                local root, path, base = split[1], split[2], split[3] +                local recurse = find(base,"%*%*") +                local start = root .. path +                local result = lpegmatch(filter,start .. base) +                globpattern(start,result,recurse,action) +                return t +            else +                return { } +            end +        end +    end +end + +dir.glob = glob + +--~ list = dir.glob("**/*.tif") +--~ list = dir.glob("/**/*.tif") +--~ list = dir.glob("./**/*.tif") +--~ list = dir.glob("oeps/**/*.tif") +--~ list = dir.glob("/oeps/**/*.tif") + +local function globfiles(path,recurse,func,files) -- func == pattern or function +    if type(func) == "string" then +        local s = func +        func = function(name) return find(name,s) end +    end +    files = files or { } +    local noffiles = #files +    for name in walkdir(path) do +        if find(name,"^%.") then +            --- skip +        else +            local mode = attributes(name,'mode') +            if mode == "directory" then +                if recurse then +                    globfiles(path .. "/" .. name,recurse,func,files) +                end +            elseif mode == "file" then +                if not func or func(name) then +                    noffiles = noffiles + 1 +                    files[noffiles] = path .. "/" .. name +                end +            end +        end +    end +    return files +end + +dir.globfiles = globfiles + +-- t = dir.glob("c:/data/develop/context/sources/**/????-*.tex") +-- t = dir.glob("c:/data/develop/tex/texmf/**/*.tex") +-- t = dir.glob("c:/data/develop/context/texmf/**/*.tex") +-- t = dir.glob("f:/minimal/tex/**/*") +-- print(dir.ls("f:/minimal/tex/**/*")) +-- print(dir.ls("*.tex")) + +function dir.ls(pattern) +    return concat(glob(pattern),"\n") +end + +--~ mkdirs("temp") +--~ mkdirs("a/b/c") +--~ mkdirs(".","/a/b/c") +--~ mkdirs("a","b","c") + +local make_indeed = true -- false + +local onwindows = os.type == "windows" or find(os.getenv("PATH"),";") + +if onwindows then + +    function dir.mkdirs(...) +        local str, pth = "", "" +        for i=1,select("#",...) do +            local s = select(i,...) +            if s == "" then +                -- skip +            elseif str == "" then +                str = s +            else +                str = str .. "/" .. s +            end +        end +        local first, middle, last +        local drive = false +        first, middle, last = match(str,"^(//)(//*)(.*)$") +        if first then +            -- empty network path == local path +        else +            first, last = match(str,"^(//)/*(.-)$") +            if first then +                middle, last = match(str,"([^/]+)/+(.-)$") +                if middle then +                    pth = "//" .. middle +                else +                    pth = "//" .. last +                    last = "" +                end +            else +                first, middle, last = match(str,"^([a-zA-Z]:)(/*)(.-)$") +                if first then +                    pth, drive = first .. middle, true +                else +                    middle, last = match(str,"^(/*)(.-)$") +                    if not middle then +                        last = str +                    end +                end +            end +        end +        for s in gmatch(last,"[^/]+") do +            if pth == "" then +                pth = s +            elseif drive then +                pth, drive = pth .. s, false +            else +                pth = pth .. "/" .. s +            end +            if make_indeed and not isdir(pth) then +                lfs.mkdir(pth) +            end +        end +        return pth, (isdir(pth) == true) +    end + +    --~ print(dir.mkdirs("","","a","c")) +    --~ print(dir.mkdirs("a")) +    --~ print(dir.mkdirs("a:")) +    --~ print(dir.mkdirs("a:/b/c")) +    --~ print(dir.mkdirs("a:b/c")) +    --~ print(dir.mkdirs("a:/bbb/c")) +    --~ print(dir.mkdirs("/a/b/c")) +    --~ print(dir.mkdirs("/aaa/b/c")) +    --~ print(dir.mkdirs("//a/b/c")) +    --~ print(dir.mkdirs("///a/b/c")) +    --~ print(dir.mkdirs("a/bbb//ccc/")) + +else + +    function dir.mkdirs(...) +        local str, pth = "", "" +        for i=1,select("#",...) do +            local s = select(i,...) +            if s and s ~= "" then -- we catch nil and false +                if str ~= "" then +                    str = str .. "/" .. s +                else +                    str = s +                end +            end +        end +        str = gsub(str,"/+","/") +        if find(str,"^/") then +            pth = "/" +            for s in gmatch(str,"[^/]+") do +                local first = (pth == "/") +                if first then +                    pth = pth .. s +                else +                    pth = pth .. "/" .. s +                end +                if make_indeed and not first and not isdir(pth) then +                    lfs.mkdir(pth) +                end +            end +        else +            pth = "." +            for s in gmatch(str,"[^/]+") do +                pth = pth .. "/" .. s +                if make_indeed and not isdir(pth) then +                    lfs.mkdir(pth) +                end +            end +        end +        return pth, (isdir(pth) == true) +    end + +    --~ print(dir.mkdirs("","","a","c")) +    --~ print(dir.mkdirs("a")) +    --~ print(dir.mkdirs("/a/b/c")) +    --~ print(dir.mkdirs("/aaa/b/c")) +    --~ print(dir.mkdirs("//a/b/c")) +    --~ print(dir.mkdirs("///a/b/c")) +    --~ print(dir.mkdirs("a/bbb//ccc/")) + +end + +dir.makedirs = dir.mkdirs + +-- we can only define it here as it uses dir.current + +if onwindows then + +    function dir.expandname(str) -- will be merged with cleanpath and collapsepath +        local first, nothing, last = match(str,"^(//)(//*)(.*)$") +        if first then +            first = dir.current() .. "/" -- dir.current sanitizes +        end +        if not first then +            first, last = match(str,"^(//)/*(.*)$") +        end +        if not first then +            first, last = match(str,"^([a-zA-Z]:)(.*)$") +            if first and not find(last,"^/") then +                local d = currentdir() +                if chdir(first) then +                    first = dir.current() +                end +                chdir(d) +            end +        end +        if not first then +            first, last = dir.current(), str +        end +        last = gsub(last,"//","/") +        last = gsub(last,"/%./","/") +        last = gsub(last,"^/*","") +        first = gsub(first,"/*$","") +        if last == "" or last == "." then +            return first +        else +            return first .. "/" .. last +        end +    end + +else + +    function dir.expandname(str) -- will be merged with cleanpath and collapsepath +        if not find(str,"^/") then +            str = currentdir() .. "/" .. str +        end +        str = gsub(str,"//","/") +        str = gsub(str,"/%./","/") +        str = gsub(str,"(.)/%.$","%1") +        return str +    end + +end + +file.expandname = dir.expandname -- for convenience + +local stack = { } + +function dir.push(newdir) +    insert(stack,currentdir()) +    if newdir and newdir ~= "" then +        chdir(newdir) +    end +end + +function dir.pop() +    local d = remove(stack) +    if d then +        chdir(d) +    end +    return d +end | 
