diff options
Diffstat (limited to 'macros/luatex/generic/penlight/pl/path.lua')
-rw-r--r-- | macros/luatex/generic/penlight/pl/path.lua | 575 |
1 files changed, 0 insertions, 575 deletions
diff --git a/macros/luatex/generic/penlight/pl/path.lua b/macros/luatex/generic/penlight/pl/path.lua deleted file mode 100644 index a438a9df87..0000000000 --- a/macros/luatex/generic/penlight/pl/path.lua +++ /dev/null @@ -1,575 +0,0 @@ ---- Path manipulation and file queries. --- --- This is modelled after Python's os.path library (10.1); see @{04-paths.md|the Guide}. --- --- NOTE: the functions assume the paths being dealt with to originate --- from the OS the application is running on. Windows drive letters are not --- to be used when running on a Unix system for example. The one exception --- is Windows paths to allow both forward and backward slashes (since Lua --- also accepts those) --- --- Dependencies: `pl.utils`, `lfs` --- @module pl.path - --- imports and locals -local _G = _G -local sub = string.sub -local getenv = os.getenv -local tmpnam = os.tmpname -local package = package -local append, concat, remove = table.insert, table.concat, table.remove -local utils = require 'pl.utils' -local assert_string,raise = utils.assert_string,utils.raise - -local res,lfs = _G.pcall(_G.require,'lfs') -if not res then - error("pl.path requires LuaFileSystem") -end - -local attrib = lfs.attributes -local currentdir = lfs.currentdir -local link_attrib = lfs.symlinkattributes - -local path = {} - -local function err_func(name, param, err, code) - local ret = ("%s failed"):format(tostring(name)) - if param ~= nil then - ret = ret .. (" for '%s'"):format(tostring(param)) - end - ret = ret .. (": %s"):format(tostring(err)) - if code ~= nil then - ret = ret .. (" (code %s)"):format(tostring(code)) - end - return ret -end - ---- Lua iterator over the entries of a given directory. --- Implicit link to [`luafilesystem.dir`](https://keplerproject.github.io/luafilesystem/manual.html#reference) --- @function dir -path.dir = lfs.dir - ---- Creates a directory. --- Implicit link to [`luafilesystem.mkdir`](https://keplerproject.github.io/luafilesystem/manual.html#reference) --- @function mkdir -path.mkdir = function(d) - local ok, err, code = lfs.mkdir(d) - if not ok then - return ok, err_func("mkdir", d, err, code), code - end - return ok, err, code -end - ---- Removes a directory. --- Implicit link to [`luafilesystem.rmdir`](https://keplerproject.github.io/luafilesystem/manual.html#reference) --- @function rmdir -path.rmdir = function(d) - local ok, err, code = lfs.rmdir(d) - if not ok then - return ok, err_func("rmdir", d, err, code), code - end - return ok, err, code -end - ---- Gets attributes. --- Implicit link to [`luafilesystem.attributes`](https://keplerproject.github.io/luafilesystem/manual.html#reference) --- @function attrib -path.attrib = function(d, r) - local ok, err, code = attrib(d, r) - if not ok then - return ok, err_func("attrib", d, err, code), code - end - return ok, err, code -end - ---- Get the working directory. --- Implicit link to [`luafilesystem.currentdir`](https://keplerproject.github.io/luafilesystem/manual.html#reference) --- @function currentdir -path.currentdir = function() - local ok, err, code = currentdir() - if not ok then - return ok, err_func("currentdir", nil, err, code), code - end - return ok, err, code -end - ---- Gets symlink attributes. --- Implicit link to [`luafilesystem.symlinkattributes`](https://keplerproject.github.io/luafilesystem/manual.html#reference) --- @function link_attrib -path.link_attrib = function(d, r) - local ok, err, code = link_attrib(d, r) - if not ok then - return ok, err_func("link_attrib", d, err, code), code - end - return ok, err, code -end - ---- Changes the working directory. --- On Windows, if a drive is specified, it also changes the current drive. If --- only specifying the drive, it will only switch drive, but not modify the path. --- Implicit link to [`luafilesystem.chdir`](https://keplerproject.github.io/luafilesystem/manual.html#reference) --- @function chdir -path.chdir = function(d) - local ok, err, code = lfs.chdir(d) - if not ok then - return ok, err_func("chdir", d, err, code), code - end - return ok, err, code -end - ---- is this a directory? --- @string P A file path -function path.isdir(P) - assert_string(1,P) - if P:match("\\$") then - P = P:sub(1,-2) - end - return attrib(P,'mode') == 'directory' -end - ---- is this a file? --- @string P A file path -function path.isfile(P) - assert_string(1,P) - return attrib(P,'mode') == 'file' -end - --- is this a symbolic link? --- @string P A file path -function path.islink(P) - assert_string(1,P) - if link_attrib then - return link_attrib(P,'mode')=='link' - else - return false - end -end - ---- return size of a file. --- @string P A file path -function path.getsize(P) - assert_string(1,P) - return attrib(P,'size') -end - ---- does a path exist? --- @string P A file path --- @return the file path if it exists (either as file, directory, socket, etc), nil otherwise -function path.exists(P) - assert_string(1,P) - return attrib(P,'mode') ~= nil and P -end - ---- Return the time of last access as the number of seconds since the epoch. --- @string P A file path -function path.getatime(P) - assert_string(1,P) - return attrib(P,'access') -end - ---- Return the time of last modification as the number of seconds since the epoch. --- @string P A file path -function path.getmtime(P) - assert_string(1,P) - return attrib(P,'modification') -end - ----Return the system's ctime as the number of seconds since the epoch. --- @string P A file path -function path.getctime(P) - assert_string(1,P) - return path.attrib(P,'change') -end - - -local function at(s,i) - return sub(s,i,i) -end - -path.is_windows = utils.is_windows - -local sep, other_sep, seps --- constant sep is the directory separator for this platform. --- constant dirsep is the separator in the PATH environment variable -if path.is_windows then - path.sep = '\\'; other_sep = '/' - path.dirsep = ';' - seps = { ['/'] = true, ['\\'] = true } -else - path.sep = '/' - path.dirsep = ':' - seps = { ['/'] = true } -end -sep = path.sep - ---- are we running Windows? --- @class field --- @name path.is_windows - ---- path separator for this platform. --- @class field --- @name path.sep - ---- separator for PATH for this platform --- @class field --- @name path.dirsep - ---- given a path, return the directory part and a file part. --- if there's no directory part, the first value will be empty --- @string P A file path --- @return directory part --- @return file part --- @usage --- local dir, file = path.splitpath("some/dir/myfile.txt") --- assert(dir == "some/dir") --- assert(file == "myfile.txt") --- --- local dir, file = path.splitpath("some/dir/") --- assert(dir == "some/dir") --- assert(file == "") --- --- local dir, file = path.splitpath("some_dir") --- assert(dir == "") --- assert(file == "some_dir") -function path.splitpath(P) - assert_string(1,P) - local i = #P - local ch = at(P,i) - while i > 0 and ch ~= sep and ch ~= other_sep do - i = i - 1 - ch = at(P,i) - end - if i == 0 then - return '',P - else - return sub(P,1,i-1), sub(P,i+1) - end -end - ---- return an absolute path. --- @string P A file path --- @string[opt] pwd optional start path to use (default is current dir) -function path.abspath(P,pwd) - assert_string(1,P) - if pwd then assert_string(2,pwd) end - local use_pwd = pwd ~= nil - if not use_pwd and not currentdir() then return P end - P = P:gsub('[\\/]$','') - pwd = pwd or currentdir() - if not path.isabs(P) then - P = path.join(pwd,P) - elseif path.is_windows and not use_pwd and at(P,2) ~= ':' and at(P,2) ~= '\\' then - P = pwd:sub(1,2)..P -- attach current drive to path like '\\fred.txt' - end - return path.normpath(P) -end - ---- given a path, return the root part and the extension part. --- if there's no extension part, the second value will be empty --- @string P A file path --- @treturn string root part (everything upto the "."", maybe empty) --- @treturn string extension part (including the ".", maybe empty) --- @usage --- local file_path, ext = path.splitext("/bonzo/dog_stuff/cat.txt") --- assert(file_path == "/bonzo/dog_stuff/cat") --- assert(ext == ".txt") --- --- local file_path, ext = path.splitext("") --- assert(file_path == "") --- assert(ext == "") -function path.splitext(P) - assert_string(1,P) - local i = #P - local ch = at(P,i) - while i > 0 and ch ~= '.' do - if seps[ch] then - return P,'' - end - i = i - 1 - ch = at(P,i) - end - if i == 0 then - return P,'' - else - return sub(P,1,i-1),sub(P,i) - end -end - ---- return the directory part of a path --- @string P A file path --- @treturn string everything before the last dir-separator --- @see splitpath --- @usage --- path.dirname("/some/path/file.txt") -- "/some/path" --- path.dirname("file.txt") -- "" (empty string) -function path.dirname(P) - assert_string(1,P) - local p1 = path.splitpath(P) - return p1 -end - ---- return the file part of a path --- @string P A file path --- @treturn string --- @see splitpath --- @usage --- path.basename("/some/path/file.txt") -- "file.txt" --- path.basename("/some/path/file/") -- "" (empty string) -function path.basename(P) - assert_string(1,P) - local _,p2 = path.splitpath(P) - return p2 -end - ---- get the extension part of a path. --- @string P A file path --- @treturn string --- @see splitext --- @usage --- path.extension("/some/path/file.txt") -- ".txt" --- path.extension("/some/path/file_txt") -- "" (empty string) -function path.extension(P) - assert_string(1,P) - local _,p2 = path.splitext(P) - return p2 -end - ---- is this an absolute path? --- @string P A file path --- @usage --- path.isabs("hello/path") -- false --- path.isabs("/hello/path") -- true --- -- Windows; --- path.isabs("hello\path") -- false --- path.isabs("\hello\path") -- true --- path.isabs("C:\hello\path") -- true --- path.isabs("C:hello\path") -- false -function path.isabs(P) - assert_string(1,P) - if path.is_windows and at(P,2) == ":" then - return seps[at(P,3)] ~= nil - end - return seps[at(P,1)] ~= nil -end - ---- return the path resulting from combining the individual paths. --- if the second (or later) path is absolute, we return the last absolute path (joined with any non-absolute paths following). --- empty elements (except the last) will be ignored. --- @string p1 A file path --- @string p2 A file path --- @string ... more file paths --- @treturn string the combined path --- @usage --- path.join("/first","second","third") -- "/first/second/third" --- path.join("first","second/third") -- "first/second/third" --- path.join("/first","/second","third") -- "/second/third" -function path.join(p1,p2,...) - assert_string(1,p1) - assert_string(2,p2) - if select('#',...) > 0 then - local p = path.join(p1,p2) - local args = {...} - for i = 1,#args do - assert_string(i,args[i]) - p = path.join(p,args[i]) - end - return p - end - if path.isabs(p2) then return p2 end - local endc = at(p1,#p1) - if endc ~= path.sep and endc ~= other_sep and endc ~= "" then - p1 = p1..path.sep - end - return p1..p2 -end - ---- normalize the case of a pathname. On Unix, this returns the path unchanged, --- for Windows it converts; --- --- * the path to lowercase --- * forward slashes to backward slashes --- @string P A file path --- @usage path.normcase("/Some/Path/File.txt") --- -- Windows: "\some\path\file.txt" --- -- Others : "/Some/Path/File.txt" -function path.normcase(P) - assert_string(1,P) - if path.is_windows then - return P:gsub('/','\\'):lower() - else - return P - end -end - ---- normalize a path name. --- `A//B`, `A/./B`, and `A/foo/../B` all become `A/B`. --- --- An empty path results in '.'. --- @string P a file path -function path.normpath(P) - assert_string(1,P) - -- Split path into anchor and relative path. - local anchor = '' - if path.is_windows then - if P:match '^\\\\' then -- UNC - anchor = '\\\\' - P = P:sub(3) - elseif seps[at(P, 1)] then - anchor = '\\' - P = P:sub(2) - elseif at(P, 2) == ':' then - anchor = P:sub(1, 2) - P = P:sub(3) - if seps[at(P, 1)] then - anchor = anchor..'\\' - P = P:sub(2) - end - end - P = P:gsub('/','\\') - else - -- According to POSIX, in path start '//' and '/' are distinct, - -- but '///+' is equivalent to '/'. - if P:match '^//' and at(P, 3) ~= '/' then - anchor = '//' - P = P:sub(3) - elseif at(P, 1) == '/' then - anchor = '/' - P = P:match '^/*(.*)$' - end - end - local parts = {} - for part in P:gmatch('[^'..sep..']+') do - if part == '..' then - if #parts ~= 0 and parts[#parts] ~= '..' then - remove(parts) - else - append(parts, part) - end - elseif part ~= '.' then - append(parts, part) - end - end - P = anchor..concat(parts, sep) - if P == '' then P = '.' end - return P -end - ---- relative path from current directory or optional start point --- @string P a path --- @string[opt] start optional start point (default current directory) -function path.relpath (P,start) - assert_string(1,P) - if start then assert_string(2,start) end - local split,min,append = utils.split, math.min, table.insert - P = path.abspath(P,start) - start = start or currentdir() - local compare - if path.is_windows then - P = P:gsub("/","\\") - start = start:gsub("/","\\") - compare = function(v) return v:lower() end - else - compare = function(v) return v end - end - local startl, Pl = split(start,sep), split(P,sep) - local n = min(#startl,#Pl) - if path.is_windows and n > 0 and at(Pl[1],2) == ':' and Pl[1] ~= startl[1] then - return P - end - local k = n+1 -- default value if this loop doesn't bail out! - for i = 1,n do - if compare(startl[i]) ~= compare(Pl[i]) then - k = i - break - end - end - local rell = {} - for i = 1, #startl-k+1 do rell[i] = '..' end - if k <= #Pl then - for i = k,#Pl do append(rell,Pl[i]) end - end - return table.concat(rell,sep) -end - - ---- Replace a starting '~' with the user's home directory. --- In windows, if HOME isn't set, then USERPROFILE is used in preference to --- HOMEDRIVE HOMEPATH. This is guaranteed to be writeable on all versions of Windows. --- @string P A file path -function path.expanduser(P) - assert_string(1,P) - if at(P,1) == '~' then - local home = getenv('HOME') - if not home then -- has to be Windows - home = getenv 'USERPROFILE' or (getenv 'HOMEDRIVE' .. getenv 'HOMEPATH') - end - return home..sub(P,2) - else - return P - end -end - - ----Return a suitable full path to a new temporary file name. --- unlike os.tmpname(), it always gives you a writeable path (uses TEMP environment variable on Windows) -function path.tmpname () - local res = tmpnam() - -- On Windows if Lua is compiled using MSVC14 os.tmpname - -- already returns an absolute path within TEMP env variable directory, - -- no need to prepend it. - if path.is_windows and not res:find(':') then - res = getenv('TEMP')..res - end - return res -end - ---- return the largest common prefix path of two paths. --- @string path1 a file path --- @string path2 a file path --- @return the common prefix (Windows: separators will be normalized, casing will be original) -function path.common_prefix (path1,path2) - assert_string(1,path1) - assert_string(2,path2) - -- get them in order! - if #path1 > #path2 then path2,path1 = path1,path2 end - local compare - if path.is_windows then - path1 = path1:gsub("/", "\\") - path2 = path2:gsub("/", "\\") - compare = function(v) return v:lower() end - else - compare = function(v) return v end - end - for i = 1,#path1 do - if compare(at(path1,i)) ~= compare(at(path2,i)) then - local cp = path1:sub(1,i-1) - if at(path1,i-1) ~= sep then - cp = path.dirname(cp) - end - return cp - end - end - if at(path2,#path1+1) ~= sep then path1 = path.dirname(path1) end - return path1 - --return '' -end - ---- return the full path where a particular Lua module would be found. --- Both package.path and package.cpath is searched, so the result may --- either be a Lua file or a shared library. --- @string mod name of the module --- @return on success: path of module, lua or binary --- @return on error: nil, error string listing paths tried -function path.package_path(mod) - assert_string(1,mod) - local res, err1, err2 - res, err1 = package.searchpath(mod,package.path) - if res then return res,true end - res, err2 = package.searchpath(mod,package.cpath) - if res then return res,false end - return raise ('cannot find module on path\n' .. err1 .. "\n" .. err2) -end - - ----- finis ----- -return path |