diff --git a/doc/path.ldoc b/doc/path.ldoc index 2365fa6..8750fa0 100644 --- a/doc/path.ldoc +++ b/doc/path.ldoc @@ -1,388 +1,388 @@ ---- --- @module path --- --- @usage --- local PATH = require "path" --- --- -- suppose we run on windows --- assert(PATH.IS_WINDOWS) --- --- -- we can use system dependet function --- print(PATH.user_home()) -- C:\Documents and Settings\Admin --- print(PATH.currentdir()) -- C:\lua\5.1 --- --- -- but we can use specific system path notation --- local ftp_path = PATH.new('/') --- print(ftp_path.join("/root", "some", "dir")) -- /root/some/dir --- --- -- All functions specific to system will fail --- assert(not pcall( function() ftp_path.currentdir() end) ) - -local path = {} - ---- The path separator. --- -path.DIR_SEP = DIR_SEP - ---- ??? --- -path.IS_WINDOWS = IS_WINDOWS - --- --- PATH manipulation - ---- Unquote path --- @local -function path.unquote(P)end - ---- Quote path --- @local -function path.quote(P)end - ---- Returns true if path has trailing path name separator. --- -function path.has_dir_end(P)end - ---- Removes the path name separator from the end of path, if it has it. --- -function path.remove_dir_end(P)end - ---- Appends a path name separator to path if one does not exist. --- -function path.ensure_dir_end(P)end - ---- Converts forward and backward slashes to @DIR_SEP --- @local -function path.normalize_sep(P) end - ---- Normalize a path. --- This function normalize path name separators and collaps --- redundant separators and up-level references. --- E.g. `/a/fred/../b`, `/a//b`,`/a\\b`, `/a/./b` all normalize to `/a/b` --- -function path.normalize(P) end - ---- Join one or more path components. --- If any component is an absolute path, all previous components --- are thrown away, and joining continues. --- If last parameter is empty string then result path will --- have trailing path name separator. --- -function path.join(...)end - ---- Split the path into root and extension. --- If there no extension then returns empty string as extension. --- If basename starts with dot then --- @usage path.splitext("/some/file.txt") --> "/some/path", ".txt" --- @usage path.splitext("/some/.passwords") --> "/some/.passwords", "" --- @usage path.splitext("/some/.file.passwords") --> "/some/.file", ".passwords" -function path.splitext(P) end - ---- Split the path into dirname and basename. --- -function path.splitpath(P)end - ---- Alias to @{splitpath}. --- -function path.split(P)end - ---- Split first path part for absolute path. --- -function path.splitroot(P)end - ---- Split path into drive specification and path. --- --- On non Windows systems drive always empty string. -function path.splitdrive(P)end - ---- Return the base name of path. --- -function path.basename(P)end - ---- Return the directory name of path. --- -function path.dirname(P)end - ---- Return the extension of path. --- -function path.extension(P)end - ---- Return first path part for absolute path. --- On Windows this is drive letter (e.g. "c:\\some\path" => "c:"). --- On *nix this is first directory name (e.g. "/usr/etc" => "/usr"). --- -function path.root(P)end - ---- Return `true` if path contain root part. --- -function path.isfullpath(P)end - ---- Alias to @{isfullpath} --- -function path.isabs(P)end - --- --- FS manipulation - ---- return user_home dir --- -function path.user_home()end - ---- Return file attributes. --- On Windows it is result of GetFileAttributesEx. --- ---
depends on `path.fs` -function path.flags(P)end - ---- Return path to temp directory. --- On Windows can use GetTempPath if Alien/FFI/afx have been found --- On Windows use TMP/TEMP environment variables --- ---
depends on `path.fs` -function path.tmpdir() end - ---- Return full path for temp file. --- ---
depends on `path.fs` -function path.tmpname()end - ---- Return size in bytes for file. --- ---
depends on `path.fs` -function path.size(P)end - ---- Alias to @{size} --- -function path.getsize(P)end - ---- Return full normalized path. --- ---
depends on `path.fs` -function path.fullpath(P)end - ---- Alias to @{fullpath}. --- -function path.abspath(P)end - ---- Return file attributes. --- --- @local ---
depends on `path.fs` -function path.attrib(P, ...)end - ---- Return path if path existing in file system. --- ---
depends on `path.fs` -function path.exists(P)end - ---- Return path if path refers to an existing directory. --- ---
depends on `path.fs` -function path.isdir(P)end - ---- Return path if path refers to an existing file. --- ---
depends on `path.fs` -function path.isfile(P)end - ---- Return path if path refers to an existing symbolic link. --- ---
depends on `path.fs` -function path.islink(P)end - ---- Return true if directory is empty. --- ---
depends on `path.fs` -function path.isempty(P)end - ---- Return creation file time. --- On *nix this is the time of the last metadata change. --- ---
depends on `path.fs` -function path.ctime(P)end - ---- Return last modification file time. --- ---
depends on `path.fs` -function path.mtime(P)end - ---- Return last access file time. --- ---
depends on `path.fs` -function path.atime(P)end - ---- Return `path.ctime` as `date` object. --- ---
depends on `path.fs` and LuaDate -function path.cdate(P)end - ---- Return `path.mtime` as `date` object. --- ---
depends on `path.fs` and LuaDate -function path.mdate(P)end - ---- Return `path.atime` as `date` object. --- ---
depends on `path.fs` and LuaDate -function path.adate(P)end - ---- Alias to @{ctime} --- -function path.getctime(P)end - ---- Alias to @{mtime} --- -function path.getmtime(P)end - ---- Alias to @{atime} --- -function path.getatime(P)end - ---- Create new directory. --- This function also creates all nested directories if needed. --- ---
depends on `path.fs` -function path.mkdir(P)end - ---- Remove empty directory. --- ---
depends on `path.fs` -function path.rmdir(P)end - ---- Remove file or empty directory. --- --- @tparam string P --- @tparam ?remove_opt opt ---
depends on `path.fs` -function path.remove(P, opt)end - ---- Rename existed file. --- ---
depends on `path.fs` -function path.rename(from,to,force)end - ---- Copy file or directory tree. --- --- @tparam string from --- @tparam string to --- @tparam ?copy_opt|boolean opt ---
depends on `path.fs` -function path.copy(from,to,opt)end - ---- Return current work directory path. --- ---
depends on `path.fs` -function path.currentdir()end - ---- Change current work directory path. --- ---
depends on `path.fs` -function path.chdir(P)end - ---- Iterate over directory tree. --- ---
depends on `path.fs` --- --- @usage -- clean currentdir --- path.each("./*", "f", path.remove, { --- delay = true; -- use snapshot of directory --- recurse = true; -- include subdirs --- reverse = true; -- subdirs at first --- }) --- --- @usage -- clean only files in currentdir --- path.each("./*", "f", path.remove, {skipdirs = true; delay = true}) --- --- @see path.fs.each --- @see path.fs.each_options --- -function path.each(str_file, str_params, func_callback, tbl_option)end - ---- Iterate over directory tree. --- ---
depends on `path.fs` --- --- @usage -- clean currentdir --- for fullpath in path.each("./*", "f", { --- delay = true; -- use snapshot of directory --- recurse = true; -- include subdirs --- reverse = true; -- subdirs at first --- })do path.remove(fullpath) end --- --- @see path.fs.each --- @see path.fs.each_options --- -function path.each(str_file, str_params, tbl_option)end - ---- Iterate over directory tree. --- ---
depends on `path.fs` --- --- @usage -- clean currentdir. for example we also retreave file mode --- path.each("./*", function(P, mode) --- if mode == 'directory' then --- path.rmdir(P) --- else --- path.remove(P) --- end --- end,{ --- param = "fm"; -- request full path and mode --- delay = true; -- use snapshot of directory --- recurse = true; -- include subdirs --- reverse = true; -- subdirs at first --- }) --- --- @see path.fs.each --- @see path.fs.each_options --- -function path.each(str_file, func_callback, tbl_option)end - ---- Iterate over directory tree. --- This is common version of function. --- ---
depends on `path.fs` --- --- @usage -- clean currentdir --- path.each("./*", { --- callback = path.remove, --- param = "f"; -- request full path --- delay = true; -- use snapshot of directory --- recurse = true; -- include subdirs --- reverse = true; -- subdirs at first --- }) --- --- @see path.fs.each --- @see path.fs.each_options --- -function path.each(str_file, tbl_option)end - ---- Iterate over directory tree. --- ---
depends on `path.fs` --- --- @see path.fs.each --- @see path.fs.each_options --- -function path.each(func_callback, tbl_option)end - ---- Option table for `path.copy` function. --- --- @table copy_opt --- @tfield[opt=false] boolean delay --- @tfield[opt=false] boolean recurse --- @tfield[opt=false] boolean skipdirs --- @tfield[opt=false] boolean skipfiles --- @tfield[opt=function(src,dst) return true end] callable accept --- @tfield[opt=function(err, src, dst) return true end] callable error --- @see path.fs.each_options - ---- Option table for `path.remove` function. --- --- @table remove_opt --- @tfield[opt=true] boolean delay --- @tfield[opt=false] boolean recurse --- @tfield[opt=false] boolean skipdirs --- @tfield[opt=false] boolean skipfiles --- @tfield[opt=function(src,dst) return true end] callable accept --- @tfield[opt=function(err, src, dst) return true end] callable error --- @see path.fs.each_options +--- +-- @module path +-- +-- @usage +-- local PATH = require "path" +-- +-- -- suppose we run on windows +-- assert(PATH.IS_WINDOWS) +-- +-- -- we can use system dependet function +-- print(PATH.user_home()) -- C:\Documents and Settings\Admin +-- print(PATH.currentdir()) -- C:\lua\5.1 +-- +-- -- but we can use specific system path notation +-- local ftp_path = PATH.new('/') +-- print(ftp_path.join("/root", "some", "dir")) -- /root/some/dir +-- +-- -- All functions specific to system will fail +-- assert(not pcall( function() ftp_path.currentdir() end) ) + +local path = {} + +--- The path separator. +-- +path.DIR_SEP = DIR_SEP + +--- ??? +-- +path.IS_WINDOWS = IS_WINDOWS + +-- +-- PATH manipulation + +--- Unquote path +-- @local +function path.unquote(P)end + +--- Quote path +-- @local +function path.quote(P)end + +--- Returns true if path has trailing path name separator. +-- +function path.has_dir_end(P)end + +--- Removes the path name separator from the end of path, if it has it. +-- +function path.remove_dir_end(P)end + +--- Appends a path name separator to path if one does not exist. +-- +function path.ensure_dir_end(P)end + +--- Converts forward and backward slashes to @DIR_SEP +-- @local +function path.normalize_sep(P) end + +--- Normalize a path. +-- This function normalize path name separators and collaps +-- redundant separators and up-level references. +-- E.g. `/a/fred/../b`, `/a//b`,`/a\\b`, `/a/./b` all normalize to `/a/b` +-- +function path.normalize(P) end + +--- Join one or more path components. +-- If any component is an absolute path, all previous components +-- are thrown away, and joining continues. +-- If last parameter is empty string then result path will +-- have trailing path name separator. +-- +function path.join(...)end + +--- Split the path into root and extension. +-- If there no extension then returns empty string as extension. +-- If basename starts with dot then +-- @usage path.splitext("/some/file.txt") --> "/some/path", ".txt" +-- @usage path.splitext("/some/.passwords") --> "/some/.passwords", "" +-- @usage path.splitext("/some/.file.passwords") --> "/some/.file", ".passwords" +function path.splitext(P) end + +--- Split the path into dirname and basename. +-- +function path.splitpath(P)end + +--- Alias to @{splitpath}. +-- +function path.split(P)end + +--- Split first path part for absolute path. +-- +function path.splitroot(P)end + +--- Split path into drive specification and path. +-- +-- On non Windows systems drive always empty string. +function path.splitdrive(P)end + +--- Return the base name of path. +-- +function path.basename(P)end + +--- Return the directory name of path. +-- +function path.dirname(P)end + +--- Return the extension of path. +-- +function path.extension(P)end + +--- Return first path part for absolute path. +-- On Windows this is drive letter (e.g. "c:\\some\path" => "c:"). +-- On *nix this is first directory name (e.g. "/usr/etc" => "/usr"). +-- +function path.root(P)end + +--- Return `true` if path contain root part. +-- +function path.isfullpath(P)end + +--- Alias to @{isfullpath} +-- +function path.isabs(P)end + +-- +-- FS manipulation + +--- return user_home dir +-- +function path.user_home()end + +--- Return file attributes. +-- On Windows it is result of GetFileAttributesEx. +-- +--
depends on `path.fs` +function path.flags(P)end + +--- Return path to temp directory. +-- On Windows can use GetTempPath if Alien/FFI/afx have been found +-- On Windows use TMP/TEMP environment variables +-- +--
depends on `path.fs` +function path.tmpdir() end + +--- Return full path for temp file. +-- +--
depends on `path.fs` +function path.tmpname()end + +--- Return size in bytes for file. +-- +--
depends on `path.fs` +function path.size(P)end + +--- Alias to @{size} +-- +function path.getsize(P)end + +--- Return full normalized path. +-- +--
depends on `path.fs` +function path.fullpath(P)end + +--- Alias to @{fullpath}. +-- +function path.abspath(P)end + +--- Return file attributes. +-- +-- @local +--
depends on `path.fs` +function path.attrib(P, ...)end + +--- Return path if path existing in file system. +-- +--
depends on `path.fs` +function path.exists(P)end + +--- Return path if path refers to an existing directory. +-- +--
depends on `path.fs` +function path.isdir(P)end + +--- Return path if path refers to an existing file. +-- +--
depends on `path.fs` +function path.isfile(P)end + +--- Return path if path refers to an existing symbolic link. +-- +--
depends on `path.fs` +function path.islink(P)end + +--- Return true if directory is empty. +-- +--
depends on `path.fs` +function path.isempty(P)end + +--- Return creation file time. +-- On *nix this is the time of the last metadata change. +-- +--
depends on `path.fs` +function path.ctime(P)end + +--- Return last modification file time. +-- +--
depends on `path.fs` +function path.mtime(P)end + +--- Return last access file time. +-- +--
depends on `path.fs` +function path.atime(P)end + +--- Return `path.ctime` as `date` object. +-- +--
depends on `path.fs` and LuaDate +function path.cdate(P)end + +--- Return `path.mtime` as `date` object. +-- +--
depends on `path.fs` and LuaDate +function path.mdate(P)end + +--- Return `path.atime` as `date` object. +-- +--
depends on `path.fs` and LuaDate +function path.adate(P)end + +--- Alias to @{ctime} +-- +function path.getctime(P)end + +--- Alias to @{mtime} +-- +function path.getmtime(P)end + +--- Alias to @{atime} +-- +function path.getatime(P)end + +--- Create new directory. +-- This function also creates all nested directories if needed. +-- +--
depends on `path.fs` +function path.mkdir(P)end + +--- Remove empty directory. +-- +--
depends on `path.fs` +function path.rmdir(P)end + +--- Remove file or empty directory. +-- +-- @tparam string P +-- @tparam ?remove_opt opt +--
depends on `path.fs` +function path.remove(P, opt)end + +--- Rename existed file. +-- +--
depends on `path.fs` +function path.rename(from,to,force)end + +--- Copy file or directory tree. +-- +-- @tparam string from +-- @tparam string to +-- @tparam ?copy_opt|boolean opt +--
depends on `path.fs` +function path.copy(from,to,opt)end + +--- Return current work directory path. +-- +--
depends on `path.fs` +function path.currentdir()end + +--- Change current work directory path. +-- +--
depends on `path.fs` +function path.chdir(P)end + +--- Iterate over directory tree. +-- +--
depends on `path.fs` +-- +-- @usage -- clean currentdir +-- path.each("./*", "f", path.remove, { +-- delay = true; -- use snapshot of directory +-- recurse = true; -- include subdirs +-- reverse = true; -- subdirs at first +-- }) +-- +-- @usage -- clean only files in currentdir +-- path.each("./*", "f", path.remove, {skipdirs = true; delay = true}) +-- +-- @see path.fs.each +-- @see path.fs.each_options +-- +function path.each(str_file, str_params, func_callback, tbl_option)end + +--- Iterate over directory tree. +-- +--
depends on `path.fs` +-- +-- @usage -- clean currentdir +-- for fullpath in path.each("./*", "f", { +-- delay = true; -- use snapshot of directory +-- recurse = true; -- include subdirs +-- reverse = true; -- subdirs at first +-- })do path.remove(fullpath) end +-- +-- @see path.fs.each +-- @see path.fs.each_options +-- +function path.each(str_file, str_params, tbl_option)end + +--- Iterate over directory tree. +-- +--
depends on `path.fs` +-- +-- @usage -- clean currentdir. for example we also retreave file mode +-- path.each("./*", function(P, mode) +-- if mode == 'directory' then +-- path.rmdir(P) +-- else +-- path.remove(P) +-- end +-- end,{ +-- param = "fm"; -- request full path and mode +-- delay = true; -- use snapshot of directory +-- recurse = true; -- include subdirs +-- reverse = true; -- subdirs at first +-- }) +-- +-- @see path.fs.each +-- @see path.fs.each_options +-- +function path.each(str_file, func_callback, tbl_option)end + +--- Iterate over directory tree. +-- This is common version of function. +-- +--
depends on `path.fs` +-- +-- @usage -- clean currentdir +-- path.each("./*", { +-- callback = path.remove, +-- param = "f"; -- request full path +-- delay = true; -- use snapshot of directory +-- recurse = true; -- include subdirs +-- reverse = true; -- subdirs at first +-- }) +-- +-- @see path.fs.each +-- @see path.fs.each_options +-- +function path.each(str_file, tbl_option)end + +--- Iterate over directory tree. +-- +--
depends on `path.fs` +-- +-- @see path.fs.each +-- @see path.fs.each_options +-- +function path.each(func_callback, tbl_option)end + +--- Option table for `path.copy` function. +-- +-- @table copy_opt +-- @tfield[opt=false] boolean delay +-- @tfield[opt=false] boolean recurse +-- @tfield[opt=false] boolean skipdirs +-- @tfield[opt=false] boolean skipfiles +-- @tfield[opt=function(src,dst) return true end] callable accept +-- @tfield[opt=function(err, src, dst) return true end] callable error +-- @see path.fs.each_options + +--- Option table for `path.remove` function. +-- +-- @table remove_opt +-- @tfield[opt=true] boolean delay +-- @tfield[opt=false] boolean recurse +-- @tfield[opt=false] boolean skipdirs +-- @tfield[opt=false] boolean skipfiles +-- @tfield[opt=function(src,dst) return true end] callable accept +-- @tfield[opt=function(err, src, dst) return true end] callable error +-- @see path.fs.each_options diff --git a/lakefile b/lakefile index 022201b..58c66c4 100644 --- a/lakefile +++ b/lakefile @@ -1,39 +1,39 @@ -PROJECT = 'path' - -J = J or path.join - -if LUA_VER == '5.3' then - LUA_DIR = ENV.LUA_DIR_5_3 or ENV.LUA_DIR - LUA_RUNNER = 'lua53' -elseif LUA_VER == '5.2' then - LUA_DIR = ENV.LUA_DIR_5_2 or ENV.LUA_DIR - LUA_RUNNER = 'lua52' -else - LUA_DIR = ENV.LUA_DIR - LUA_RUNNER = 'lua' -end - -ROOT = ROOT or J(LUA_DIR,'libs',PROJECT) -LUADIR = LUADIR or J(ROOT, 'lua') - -install = target('install', { - file.group{odir= LUADIR; recurse = true; src = J('lua', '*.*' )}; - file.group{odir=J(ROOT, 'test'); recurse = true; src = J('test', '*.*' )}; -}) - -local function run(file, cwd) - print() - print("run " .. file) - if not TESTING then - if cwd then lake.chdir(cwd) end - os.execute( LUA_RUNNER .. ' ' .. file ) - if cwd then lake.chdir("<") end - print() - end -end - -target('test', install, function() - local test_dir = J(ROOT,'test') - run(J(test_dir,'run.lua'), test_dir) - run(J(test_dir,'test_lfs.lua'), test_dir) -end) +PROJECT = 'path' + +J = J or path.join + +if LUA_VER == '5.3' then + LUA_DIR = ENV.LUA_DIR_5_3 or ENV.LUA_DIR + LUA_RUNNER = 'lua53' +elseif LUA_VER == '5.2' then + LUA_DIR = ENV.LUA_DIR_5_2 or ENV.LUA_DIR + LUA_RUNNER = 'lua52' +else + LUA_DIR = ENV.LUA_DIR + LUA_RUNNER = 'lua' +end + +ROOT = ROOT or J(LUA_DIR,'libs',PROJECT) +LUADIR = LUADIR or J(ROOT, 'lua') + +install = target('install', { + file.group{odir= LUADIR; recurse = true; src = J('lua', '*.*' )}; + file.group{odir=J(ROOT, 'test'); recurse = true; src = J('test', '*.*' )}; +}) + +local function run(file, cwd) + print() + print("run " .. file) + if not TESTING then + if cwd then lake.chdir(cwd) end + os.execute( LUA_RUNNER .. ' ' .. file ) + if cwd then lake.chdir("<") end + print() + end +end + +target('test', install, function() + local test_dir = J(ROOT,'test') + run(J(test_dir,'run.lua'), test_dir) + run(J(test_dir,'test_lfs.lua'), test_dir) +end) diff --git a/lua/path.lua b/lua/path.lua index 29464a3..a77df69 100644 --- a/lua/path.lua +++ b/lua/path.lua @@ -1,537 +1,537 @@ -local package = require "package" -local string = require "string" -local table = require "table" -local os = require "os" -local io = require "io" - -local USE_ALIEN = true -local USE_FFI = true -local USE_AFX = true - -local DIR_SEP = package.config:sub(1,1) -local IS_WINDOWS = DIR_SEP == '\\' - -local PATH = {} - -PATH.DIR_SEP = DIR_SEP -PATH.IS_WINDOWS = IS_WINDOWS - --- --- PATH manipulation - -function PATH:unquote(P) - if P:sub(1,1) == '"' and P:sub(-1,-1) == '"' then - return (P:sub(2,-2)) - end - return P -end - -function PATH:quote(P) - if P:find("%s") then - return '"' .. P .. '"' - end - return P -end - -function PATH:has_dir_end(P) - return (string.find(P, '[\\/]$')) and true -end - -function PATH:remove_dir_end(P) - return (string.gsub(P, '[\\/]+$', '')) -end - -function PATH:ensure_dir_end(P) - return self:remove_dir_end(P) .. self.DIR_SEP -end - -function PATH:isunc(P) - return (string.sub(P, 1, 2) == (self.DIR_SEP .. self.DIR_SEP)) and P -end - -function PATH:normolize_sep(P) - return (string.gsub(P, '\\', self.DIR_SEP):gsub('/', self.DIR_SEP)) -end - -PATH.normalize_sep = PATH.normolize_sep - -function PATH:normolize(P) - P = self:normolize_sep(P) - local DIR_SEP = self.DIR_SEP - - local is_unc = self:isunc(P) - while true do -- `/./` => `/` - local n P,n = string.gsub(P, DIR_SEP .. '%.' .. DIR_SEP, DIR_SEP) - if n == 0 then break end - end - while true do -- `//` => `/` - local n P,n = string.gsub(P, DIR_SEP .. DIR_SEP, DIR_SEP) - if n == 0 then break end - end - P = string.gsub(P, DIR_SEP .. '%.$', '') - if (not IS_WINDOWS) and (P == '') then P = '/' end - - if is_unc then P = DIR_SEP .. P end - - local root, path = nil, P - if is_unc then - root, path = self:splitroot(P) - end - - path = self:ensure_dir_end(path) - while true do - local first, last = string.find(path, DIR_SEP .. "[^".. DIR_SEP .. "]+" .. DIR_SEP .. '%.%.' .. DIR_SEP) - if not first then break end - path = string.sub(path, 1, first) .. string.sub(path, last+1) - end - P = path - - if root then -- unc - assert(is_unc) - P = P:gsub( '%.%.?' .. DIR_SEP , '') - P = DIR_SEP .. DIR_SEP .. self:join(root, P) - elseif self.IS_WINDOWS then - -- c:\..\foo => c:\foo - -- \..\foo => \foo - local root, path = self:splitroot(P) - if root ~= '' or P:sub(1,1) == DIR_SEP then - path = path:gsub( '%.%.?' .. DIR_SEP , '') - P = self:join(root, path) - end - end - - if self.IS_WINDOWS and #P <= 3 and P:sub(2,2) == ':' then -- c: => c:\ or c:\ => c:\ - if #P == 2 then return P .. self.DIR_SEP end - return P - end - - if (not self.IS_WINDOWS) and (P == DIR_SEP) then return '/' end - return self:remove_dir_end(P) -end - -PATH.normalize = PATH.normolize - -function PATH:join_(P1, P2) - local ch = P2:sub(1,1) - if (ch == '\\') or (ch == '/') then - return self:remove_dir_end(P1) .. P2 - end - return self:ensure_dir_end(P1) .. P2 -end - -function PATH:join(...) - local t,n = {...}, select('#', ...) - local r = t[1] - for i = 2, #t do - if self:isfullpath(t[i]) then - r = t[i] - else - r = self:join_(r,t[i]) - end - end - return r -end - -function PATH:splitext(P) - local s1,s2 = string.match(P,"(.-[^\\/.])(%.[^\\/.]*)$") - if s1 then return s1,s2 end - return P, '' -end - -function PATH:splitpath(P) - return string.match(P,"^(.-)[\\/]?([^\\/]*)$") -end - -function PATH:splitroot(P) - if self.IS_WINDOWS then - if self:isunc(P) then - return string.match(P, [[^\\([^\/]+)[\]?(.*)$]]) - end - if string.sub(P,2,2) == ':' then - return string.sub(P,1,2), string.sub(P,4) - end - return '', P - else - if string.sub(P,1,1) == '/' then - return string.match(P,[[^/([^\/]+)[/]?(.*)$]]) - end - return '', P - end -end - -function PATH:splitdrive(P) - if self.IS_WINDOWS then - return self:splitroot(P) - end - return '', P -end - -function PATH:basename(P) - local s1,s2 = self:splitpath(P) - return s2 -end - -function PATH:dirname(P) - return (self:splitpath(P)) -end - -function PATH:extension(P) - local s1,s2 = self:splitext(P) - return s2 -end - -function PATH:root(P) - return (self:splitroot(P)) -end - -function PATH:isfullpath(P) - return (self:root(P) ~= '') and P -end - -function PATH:user_home() - if IS_WINDOWS then - return os.getenv('USERPROFILE') or PATH:join(os.getenv('HOMEDRIVE'), os.getenv('HOMEPATH')) - end - return os.getenv('HOME') -end - -local function prequire(m) - local ok, err = pcall(require, m) - if not ok then return nil, err end - return err -end - -local fs = prequire "path.fs" - -if fs then - --- --- PATH based on system - -local function assert_system(self) - if PATH.IS_WINDOWS then assert(self.IS_WINDOWS) return end - assert(not self.IS_WINDOWS) -end - -if fs.flags then - function PATH:flags(P, ...) - assert_system(self) - P = self:fullpath(P) - return fs.flags(P, ...) - end -end - -function PATH:tmpdir() - assert_system(self) - return self:remove_dir_end(fs.tmpdir()) -end - -function PATH:tmpname() - local P = os.tmpname() - if self:dirname(P) == '' then - P = self:join(self:tmpdir(), P) - end - return P -end - -function PATH:size(P) - assert_system(self) - return fs.size(P) -end - -function PATH:fullpath(P) - if not self:isfullpath(P) then - P = self:normolize_sep(P) - local ch1, ch2 = P:sub(1,1), P:sub(2,2) - if ch1 == '~' then -- ~\temp - P = self:join(self:user_home(), P:sub(2)) - elseif self.IS_WINDOWS and (ch1 == self.DIR_SEP) then -- \temp => c:\temp - local root = self:root(self:currentdir()) - P = self:join(root, P) - else - P = self:join(self:currentdir(), P) - end - end - - return self:normolize(P) -end - -function PATH:attrib(P, ...) - assert_system(self) - return fs.attributes(P, ...) -end - -function PATH:exists(P) - assert_system(self) - return fs.exists(self:fullpath(P)) -end - -function PATH:isdir(P) - assert_system(self) - return fs.isdir(self:fullpath(P)) -end - -function PATH:isfile(P) - assert_system(self) - return fs.isfile(self:fullpath(P)) -end - -function PATH:islink(P) - assert_system(self) - return fs.islink(self:fullpath(P)) -end - -function PATH:ctime(P) - assert_system(self) - return fs.ctime(self:fullpath(P)) -end - -function PATH:mtime(P) - assert_system(self) - return fs.mtime(self:fullpath(P)) -end - -function PATH:atime(P) - assert_system(self) - return fs.atime(self:fullpath(P)) -end - -function PATH:touch(P, ...) - assert_system(self) - return fs.touch(self:fullpath(P), ...) -end - -function PATH:currentdir() - assert_system(self) - return self:normolize(fs.currentdir()) -end - -function PATH:chdir(P) - assert_system(self) - return fs.chdir(self:fullpath(P)) -end - -function PATH:isempty(P) - assert_system(self) - local ok, err = fs.each_impl{ - file = self:ensure_dir_end(P), - callback = function() return 'pass' end; - } - if err then return nil, err end - return ok ~= 'pass' -end - -local date = prequire "date" -if date then - local function make_getfiletime_as_date(fn) - if date then - return function(...) - local t,e = fn(...) - if not t then return nil, e end - return date(t) - end - end - end - - PATH.cdate = make_getfiletime_as_date( PATH.ctime ); - PATH.mdate = make_getfiletime_as_date( PATH.mtime ); - PATH.adate = make_getfiletime_as_date( PATH.atime ); -end - -function PATH:mkdir(P) - assert_system(self) - local P = self:fullpath(P) - if self:exists(P) then return self:isdir(P) end - local p = '' - P = self:ensure_dir_end(P) - for str in string.gmatch(P, '.-' .. self.DIR_SEP) do - p = p .. str - if self:exists(p) then - if not self:isdir(p) then - return nil, 'can not create ' .. p - end - else - if IS_WINDOWS or p ~= DIR_SEP then - local ok, err = fs.mkdir(self:remove_dir_end(p)) - if not ok then return nil, err .. ' ' .. p end - end - end - end - return P -end - -function PATH:rmdir(P) - assert_system(self) - return fs.rmdir(self:fullpath(P)) -end - -function PATH:rename(from, to, force) - assert_system(self) - from = self:fullpath(from) - to = self:fullpath(to) - return fs.move(from, to, force) -end - -local each = require "path.findfile".load(function(opt) - local has_dir_end = PATH:has_dir_end(opt.file) - opt.file = PATH:fullpath(opt.file) - if has_dir_end then opt.file = PATH:ensure_dir_end(opt.file) end - return fs.each_impl(opt) -end) - -function PATH:each(...) - assert_system(self) - return each(...) -end - -local function copy_impl_batch(fs, src_dir, mask, dst_dir, opt) - if not opt then opt = {} end - - local overwrite = opt.overwrite - local accept = opt.accept - local onerror = opt.error - local chlen = #fs.DIR_SEP - local count = 0 - - local ok, err = fs.each_impl{file = src_dir .. fs.DIR_SEP .. mask, - delay = opt.delay; recurse = opt.recurse; param = "pnm"; - skipdirs = opt.skipdirs; skipfiles = opt.skipfiles; - callback = function(path, name, mode) - local rel = string.sub(path, #src_dir + chlen + 1) - if #rel > 0 then rel = rel .. fs.DIR_SEP .. name else rel = name end - local dst = dst_dir .. fs.DIR_SEP .. rel - local src = path .. fs.DIR_SEP .. name - - if accept then - local ok = accept(src, dst, opt) - if not ok then return end - end - - local ok, err - if mode == "directory" then ok, err = fs.mkdir(dst) - else ok, err = fs.copy(src, dst, not overwrite) end - - if not ok and onerror then - if not onerror(err, src, dst, opt) then -- break - return true - end - else - count = count + 1 - end - end; - } - if ok or err then return ok, err end - return count -end - -local function remove_impl_batch(fs, src_dir, mask, opt) - if not opt then opt = {} end - - local overwrite = opt.overwrite - local accept = opt.accept - local onerror = opt.error - local chlen = #fs.DIR_SEP - local count = 0 - local delay = (opt.delay == nil) and true or opt.delay - - local ok, err = fs.each_impl{file = src_dir .. fs.DIR_SEP .. mask, - delay = delay; recurse = opt.recurse; reverse = true; param = "fm"; - skipdirs = opt.skipdirs; skipfiles = opt.skipfiles; - callback = function(src, mode) - if accept then - local ok = accept(src, opt) - if not ok then return end - end - - local ok, err - if mode == "directory" then ok, err = fs.rmdir(src) - else ok, err = fs.remove(src) end - - if not ok and onerror then - if not onerror(err, src, opt) then -- break - return true - end - else - count = count + 1 - end - end; - } - if ok or err then return ok, err end - return count -end - -function PATH:remove_impl(P) - if self:isdir(P) then return fs.rmdir(P) end - return fs.remove(P) -end - -function PATH:copy(from, to, opt) - from = self:fullpath(from) - to = self:fullpath(to) - - if type(opt) == "boolean" then opt = {overwrite = opt} end - - local overwrite = opt and opt.overwrite - local recurse = opt and opt.recurse - - local src_dir, src_name = self:splitpath(from) - if recurse or src_name:find("[*?]") then -- batch mode - self:mkdir(to) - return copy_impl_batch(fs, src_dir, src_name, to, opt) - end - if self.mkdir then self:mkdir(self:dirname(to)) end - return fs.copy(from, to, not not overwrite) -end - -function PATH:remove(P, opt) - assert_system(self) - local P = self:fullpath(P) - local dir, name = self:splitpath(P) - if (opt and opt.recurse) or name:find("[*?]") then -- batch mode - return remove_impl_batch(fs, dir, name, opt) - end - return self:remove_impl(P) -end - -end -- fs - -do -- Python aliases -PATH.split = PATH.splitpath -PATH.isabs = PATH.isfullpath -PATH.normpath = PATH.normolize -PATH.abspath = PATH.fullpath -PATH.getctime = PATH.ctime -PATH.getatime = PATH.atime -PATH.getmtime = PATH.mtime -PATH.getsize = PATH.size -end - -local function make_module() - local M = require "path.module" - for k, f in pairs(PATH) do - if type(f) == 'function' then - M[k] = function(...) return f(PATH, ...) end - else - M[k] = f - end - end - return M -end - -local M = make_module() - -function M.new(DIR_SEP) - local o = setmetatable({}, {__index = PATH}) - if type(DIR_SEP) == 'string' then - o.DIR_SEP = DIR_SEP - o.IS_WINDOWS = (DIR_SEP == '\\') - else - assert(type(DIR_SEP) == 'boolean') - o.IS_WINDOWS = DIR_SEP - o.DIR_SEP = o.IS_WINDOWS and '\\' or '/' - end - - return o -end - -return M +local package = require "package" +local string = require "string" +local table = require "table" +local os = require "os" +local io = require "io" + +local USE_ALIEN = true +local USE_FFI = true +local USE_AFX = true + +local DIR_SEP = package.config:sub(1,1) +local IS_WINDOWS = DIR_SEP == '\\' + +local PATH = {} + +PATH.DIR_SEP = DIR_SEP +PATH.IS_WINDOWS = IS_WINDOWS + +-- +-- PATH manipulation + +function PATH:unquote(P) + if P:sub(1,1) == '"' and P:sub(-1,-1) == '"' then + return (P:sub(2,-2)) + end + return P +end + +function PATH:quote(P) + if P:find("%s") then + return '"' .. P .. '"' + end + return P +end + +function PATH:has_dir_end(P) + return (string.find(P, '[\\/]$')) and true +end + +function PATH:remove_dir_end(P) + return (string.gsub(P, '[\\/]+$', '')) +end + +function PATH:ensure_dir_end(P) + return self:remove_dir_end(P) .. self.DIR_SEP +end + +function PATH:isunc(P) + return (string.sub(P, 1, 2) == (self.DIR_SEP .. self.DIR_SEP)) and P +end + +function PATH:normolize_sep(P) + return (string.gsub(P, '\\', self.DIR_SEP):gsub('/', self.DIR_SEP)) +end + +PATH.normalize_sep = PATH.normolize_sep + +function PATH:normolize(P) + P = self:normolize_sep(P) + local DIR_SEP = self.DIR_SEP + + local is_unc = self:isunc(P) + while true do -- `/./` => `/` + local n P,n = string.gsub(P, DIR_SEP .. '%.' .. DIR_SEP, DIR_SEP) + if n == 0 then break end + end + while true do -- `//` => `/` + local n P,n = string.gsub(P, DIR_SEP .. DIR_SEP, DIR_SEP) + if n == 0 then break end + end + P = string.gsub(P, DIR_SEP .. '%.$', '') + if (not IS_WINDOWS) and (P == '') then P = '/' end + + if is_unc then P = DIR_SEP .. P end + + local root, path = nil, P + if is_unc then + root, path = self:splitroot(P) + end + + path = self:ensure_dir_end(path) + while true do + local first, last = string.find(path, DIR_SEP .. "[^".. DIR_SEP .. "]+" .. DIR_SEP .. '%.%.' .. DIR_SEP) + if not first then break end + path = string.sub(path, 1, first) .. string.sub(path, last+1) + end + P = path + + if root then -- unc + assert(is_unc) + P = P:gsub( '%.%.?' .. DIR_SEP , '') + P = DIR_SEP .. DIR_SEP .. self:join(root, P) + elseif self.IS_WINDOWS then + -- c:\..\foo => c:\foo + -- \..\foo => \foo + local root, path = self:splitroot(P) + if root ~= '' or P:sub(1,1) == DIR_SEP then + path = path:gsub( '%.%.?' .. DIR_SEP , '') + P = self:join(root, path) + end + end + + if self.IS_WINDOWS and #P <= 3 and P:sub(2,2) == ':' then -- c: => c:\ or c:\ => c:\ + if #P == 2 then return P .. self.DIR_SEP end + return P + end + + if (not self.IS_WINDOWS) and (P == DIR_SEP) then return '/' end + return self:remove_dir_end(P) +end + +PATH.normalize = PATH.normolize + +function PATH:join_(P1, P2) + local ch = P2:sub(1,1) + if (ch == '\\') or (ch == '/') then + return self:remove_dir_end(P1) .. P2 + end + return self:ensure_dir_end(P1) .. P2 +end + +function PATH:join(...) + local t,n = {...}, select('#', ...) + local r = t[1] + for i = 2, #t do + if self:isfullpath(t[i]) then + r = t[i] + else + r = self:join_(r,t[i]) + end + end + return r +end + +function PATH:splitext(P) + local s1,s2 = string.match(P,"(.-[^\\/.])(%.[^\\/.]*)$") + if s1 then return s1,s2 end + return P, '' +end + +function PATH:splitpath(P) + return string.match(P,"^(.-)[\\/]?([^\\/]*)$") +end + +function PATH:splitroot(P) + if self.IS_WINDOWS then + if self:isunc(P) then + return string.match(P, [[^\\([^\/]+)[\]?(.*)$]]) + end + if string.sub(P,2,2) == ':' then + return string.sub(P,1,2), string.sub(P,4) + end + return '', P + else + if string.sub(P,1,1) == '/' then + return string.match(P,[[^/([^\/]+)[/]?(.*)$]]) + end + return '', P + end +end + +function PATH:splitdrive(P) + if self.IS_WINDOWS then + return self:splitroot(P) + end + return '', P +end + +function PATH:basename(P) + local s1,s2 = self:splitpath(P) + return s2 +end + +function PATH:dirname(P) + return (self:splitpath(P)) +end + +function PATH:extension(P) + local s1,s2 = self:splitext(P) + return s2 +end + +function PATH:root(P) + return (self:splitroot(P)) +end + +function PATH:isfullpath(P) + return (self:root(P) ~= '') and P +end + +function PATH:user_home() + if IS_WINDOWS then + return os.getenv('USERPROFILE') or PATH:join(os.getenv('HOMEDRIVE'), os.getenv('HOMEPATH')) + end + return os.getenv('HOME') +end + +local function prequire(m) + local ok, err = pcall(require, m) + if not ok then return nil, err end + return err +end + +local fs = prequire "path.fs" + +if fs then + +-- +-- PATH based on system + +local function assert_system(self) + if PATH.IS_WINDOWS then assert(self.IS_WINDOWS) return end + assert(not self.IS_WINDOWS) +end + +if fs.flags then + function PATH:flags(P, ...) + assert_system(self) + P = self:fullpath(P) + return fs.flags(P, ...) + end +end + +function PATH:tmpdir() + assert_system(self) + return self:remove_dir_end(fs.tmpdir()) +end + +function PATH:tmpname() + local P = os.tmpname() + if self:dirname(P) == '' then + P = self:join(self:tmpdir(), P) + end + return P +end + +function PATH:size(P) + assert_system(self) + return fs.size(P) +end + +function PATH:fullpath(P) + if not self:isfullpath(P) then + P = self:normolize_sep(P) + local ch1, ch2 = P:sub(1,1), P:sub(2,2) + if ch1 == '~' then -- ~\temp + P = self:join(self:user_home(), P:sub(2)) + elseif self.IS_WINDOWS and (ch1 == self.DIR_SEP) then -- \temp => c:\temp + local root = self:root(self:currentdir()) + P = self:join(root, P) + else + P = self:join(self:currentdir(), P) + end + end + + return self:normolize(P) +end + +function PATH:attrib(P, ...) + assert_system(self) + return fs.attributes(P, ...) +end + +function PATH:exists(P) + assert_system(self) + return fs.exists(self:fullpath(P)) +end + +function PATH:isdir(P) + assert_system(self) + return fs.isdir(self:fullpath(P)) +end + +function PATH:isfile(P) + assert_system(self) + return fs.isfile(self:fullpath(P)) +end + +function PATH:islink(P) + assert_system(self) + return fs.islink(self:fullpath(P)) +end + +function PATH:ctime(P) + assert_system(self) + return fs.ctime(self:fullpath(P)) +end + +function PATH:mtime(P) + assert_system(self) + return fs.mtime(self:fullpath(P)) +end + +function PATH:atime(P) + assert_system(self) + return fs.atime(self:fullpath(P)) +end + +function PATH:touch(P, ...) + assert_system(self) + return fs.touch(self:fullpath(P), ...) +end + +function PATH:currentdir() + assert_system(self) + return self:normolize(fs.currentdir()) +end + +function PATH:chdir(P) + assert_system(self) + return fs.chdir(self:fullpath(P)) +end + +function PATH:isempty(P) + assert_system(self) + local ok, err = fs.each_impl{ + file = self:ensure_dir_end(P), + callback = function() return 'pass' end; + } + if err then return nil, err end + return ok ~= 'pass' +end + +local date = prequire "date" +if date then + local function make_getfiletime_as_date(fn) + if date then + return function(...) + local t,e = fn(...) + if not t then return nil, e end + return date(t) + end + end + end + + PATH.cdate = make_getfiletime_as_date( PATH.ctime ); + PATH.mdate = make_getfiletime_as_date( PATH.mtime ); + PATH.adate = make_getfiletime_as_date( PATH.atime ); +end + +function PATH:mkdir(P) + assert_system(self) + local P = self:fullpath(P) + if self:exists(P) then return self:isdir(P) end + local p = '' + P = self:ensure_dir_end(P) + for str in string.gmatch(P, '.-' .. self.DIR_SEP) do + p = p .. str + if self:exists(p) then + if not self:isdir(p) then + return nil, 'can not create ' .. p + end + else + if IS_WINDOWS or p ~= DIR_SEP then + local ok, err = fs.mkdir(self:remove_dir_end(p)) + if not ok then return nil, err .. ' ' .. p end + end + end + end + return P +end + +function PATH:rmdir(P) + assert_system(self) + return fs.rmdir(self:fullpath(P)) +end + +function PATH:rename(from, to, force) + assert_system(self) + from = self:fullpath(from) + to = self:fullpath(to) + return fs.move(from, to, force) +end + +local each = require "path.findfile".load(function(opt) + local has_dir_end = PATH:has_dir_end(opt.file) + opt.file = PATH:fullpath(opt.file) + if has_dir_end then opt.file = PATH:ensure_dir_end(opt.file) end + return fs.each_impl(opt) +end) + +function PATH:each(...) + assert_system(self) + return each(...) +end + +local function copy_impl_batch(fs, src_dir, mask, dst_dir, opt) + if not opt then opt = {} end + + local overwrite = opt.overwrite + local accept = opt.accept + local onerror = opt.error + local chlen = #fs.DIR_SEP + local count = 0 + + local ok, err = fs.each_impl{file = src_dir .. fs.DIR_SEP .. mask, + delay = opt.delay; recurse = opt.recurse; param = "pnm"; + skipdirs = opt.skipdirs; skipfiles = opt.skipfiles; + callback = function(path, name, mode) + local rel = string.sub(path, #src_dir + chlen + 1) + if #rel > 0 then rel = rel .. fs.DIR_SEP .. name else rel = name end + local dst = dst_dir .. fs.DIR_SEP .. rel + local src = path .. fs.DIR_SEP .. name + + if accept then + local ok = accept(src, dst, opt) + if not ok then return end + end + + local ok, err + if mode == "directory" then ok, err = fs.mkdir(dst) + else ok, err = fs.copy(src, dst, not overwrite) end + + if not ok and onerror then + if not onerror(err, src, dst, opt) then -- break + return true + end + else + count = count + 1 + end + end; + } + if ok or err then return ok, err end + return count +end + +local function remove_impl_batch(fs, src_dir, mask, opt) + if not opt then opt = {} end + + local overwrite = opt.overwrite + local accept = opt.accept + local onerror = opt.error + local chlen = #fs.DIR_SEP + local count = 0 + local delay = (opt.delay == nil) and true or opt.delay + + local ok, err = fs.each_impl{file = src_dir .. fs.DIR_SEP .. mask, + delay = delay; recurse = opt.recurse; reverse = true; param = "fm"; + skipdirs = opt.skipdirs; skipfiles = opt.skipfiles; + callback = function(src, mode) + if accept then + local ok = accept(src, opt) + if not ok then return end + end + + local ok, err + if mode == "directory" then ok, err = fs.rmdir(src) + else ok, err = fs.remove(src) end + + if not ok and onerror then + if not onerror(err, src, opt) then -- break + return true + end + else + count = count + 1 + end + end; + } + if ok or err then return ok, err end + return count +end + +function PATH:remove_impl(P) + if self:isdir(P) then return fs.rmdir(P) end + return fs.remove(P) +end + +function PATH:copy(from, to, opt) + from = self:fullpath(from) + to = self:fullpath(to) + + if type(opt) == "boolean" then opt = {overwrite = opt} end + + local overwrite = opt and opt.overwrite + local recurse = opt and opt.recurse + + local src_dir, src_name = self:splitpath(from) + if recurse or src_name:find("[*?]") then -- batch mode + self:mkdir(to) + return copy_impl_batch(fs, src_dir, src_name, to, opt) + end + if self.mkdir then self:mkdir(self:dirname(to)) end + return fs.copy(from, to, not not overwrite) +end + +function PATH:remove(P, opt) + assert_system(self) + local P = self:fullpath(P) + local dir, name = self:splitpath(P) + if (opt and opt.recurse) or name:find("[*?]") then -- batch mode + return remove_impl_batch(fs, dir, name, opt) + end + return self:remove_impl(P) +end + +end -- fs + +do -- Python aliases +PATH.split = PATH.splitpath +PATH.isabs = PATH.isfullpath +PATH.normpath = PATH.normolize +PATH.abspath = PATH.fullpath +PATH.getctime = PATH.ctime +PATH.getatime = PATH.atime +PATH.getmtime = PATH.mtime +PATH.getsize = PATH.size +end + +local function make_module() + local M = require "path.module" + for k, f in pairs(PATH) do + if type(f) == 'function' then + M[k] = function(...) return f(PATH, ...) end + else + M[k] = f + end + end + return M +end + +local M = make_module() + +function M.new(DIR_SEP) + local o = setmetatable({}, {__index = PATH}) + if type(DIR_SEP) == 'string' then + o.DIR_SEP = DIR_SEP + o.IS_WINDOWS = (DIR_SEP == '\\') + else + assert(type(DIR_SEP) == 'boolean') + o.IS_WINDOWS = DIR_SEP + o.DIR_SEP = o.IS_WINDOWS and '\\' or '/' + end + + return o +end + +return M diff --git a/lua/path/fs.lua b/lua/path/fs.lua index 5017ccc..cd4a66c 100644 --- a/lua/path/fs.lua +++ b/lua/path/fs.lua @@ -1,29 +1,29 @@ -local DIR_SEP = package.config:sub(1,1) -local IS_WINDOWS = DIR_SEP == '\\' - -local function prequire(m) - local ok, err = pcall(require, m) - if not ok then return nil, err end - return err -end - -local fs - -if not fs and IS_WINDOWS then - local fsload = require"path.win32.fs".load - local ok, mod = pcall(fsload, "ffi", "A") - if not ok then ok, mod = pcall(fsload, "alien", "A") end - fs = ok and mod -end - -if not fs and not IS_WINDOWS then - fs = prequire"path.syscall.fs" -end - -if not fs then - fs = prequire"path.lfs.fs" -end - -assert(fs, "you need installed LuaFileSystem or FFI/Alien (Windows only)") - -return fs +local DIR_SEP = package.config:sub(1,1) +local IS_WINDOWS = DIR_SEP == '\\' + +local function prequire(m) + local ok, err = pcall(require, m) + if not ok then return nil, err end + return err +end + +local fs + +if not fs and IS_WINDOWS then + local fsload = require"path.win32.fs".load + local ok, mod = pcall(fsload, "ffi", "A") + if not ok then ok, mod = pcall(fsload, "alien", "A") end + fs = ok and mod +end + +if not fs and not IS_WINDOWS then + fs = prequire"path.syscall.fs" +end + +if not fs then + fs = prequire"path.lfs.fs" +end + +assert(fs, "you need installed LuaFileSystem or FFI/Alien (Windows only)") + +return fs diff --git a/rockspecs/lua-path-scm-0.rockspec b/rockspecs/lua-path-scm-0.rockspec index 080081c..27e1cc7 100644 --- a/rockspecs/lua-path-scm-0.rockspec +++ b/rockspecs/lua-path-scm-0.rockspec @@ -1,48 +1,48 @@ -package = "lua-path" -version = "scm-0" -source = { - url = "https://github.com/moteus/lua-path/archive/master.zip", - dir = "lua-path-master", -} - -description = { - summary = "File system path manipulation library", - detailed = [[ - ]], - homepage = "https://github.com/moteus/lua-path", - license = "MIT/X11", -} - -dependencies = { - "lua >= 5.1, < 5.4", - -- "luafilesystem >= 1.4", - -- "alien >= 0.7.0", -- instead lfs on windows -} - -build = { - type = "builtin", - copy_directories = { - "test", - }, - modules = { - ["path" ] = "lua/path.lua", - ["path.fs" ] = "lua/path/fs.lua", - ["path.findfile" ] = "lua/path/findfile.lua", - ["path.lfs.fs" ] = "lua/path/lfs/fs.lua", - ["path.syscall.fs" ] = "lua/path/syscall/fs.lua", - ["path.lfs.impl.fs" ] = "lua/path/lfs/impl/fs.lua", - ["path.module" ] = "lua/path/module.lua", - ["path.win32.alien.fs" ] = "lua/path/win32/alien/fs.lua", - ["path.win32.alien.types" ] = "lua/path/win32/alien/types.lua", - ["path.win32.alien.utils" ] = "lua/path/win32/alien/utils.lua", - ["path.win32.alien.wcs" ] = "lua/path/win32/alien/wcs.lua", - ["path.win32.ffi.fs" ] = "lua/path/win32/ffi/fs.lua", - ["path.win32.ffi.types" ] = "lua/path/win32/ffi/types.lua", - ["path.win32.ffi.wcs" ] = "lua/path/win32/ffi/wcs.lua", - ["path.win32.fs" ] = "lua/path/win32/fs.lua", - ["path.win32.wcs" ] = "lua/path/win32/wcs.lua", - } -} - - - +package = "lua-path" +version = "scm-0" +source = { + url = "https://github.com/moteus/lua-path/archive/master.zip", + dir = "lua-path-master", +} + +description = { + summary = "File system path manipulation library", + detailed = [[ + ]], + homepage = "https://github.com/moteus/lua-path", + license = "MIT/X11", +} + +dependencies = { + "lua >= 5.1, < 5.4", + -- "luafilesystem >= 1.4", + -- "alien >= 0.7.0", -- instead lfs on windows +} + +build = { + type = "builtin", + copy_directories = { + "test", + }, + modules = { + ["path" ] = "lua/path.lua", + ["path.fs" ] = "lua/path/fs.lua", + ["path.findfile" ] = "lua/path/findfile.lua", + ["path.lfs.fs" ] = "lua/path/lfs/fs.lua", + ["path.syscall.fs" ] = "lua/path/syscall/fs.lua", + ["path.lfs.impl.fs" ] = "lua/path/lfs/impl/fs.lua", + ["path.module" ] = "lua/path/module.lua", + ["path.win32.alien.fs" ] = "lua/path/win32/alien/fs.lua", + ["path.win32.alien.types" ] = "lua/path/win32/alien/types.lua", + ["path.win32.alien.utils" ] = "lua/path/win32/alien/utils.lua", + ["path.win32.alien.wcs" ] = "lua/path/win32/alien/wcs.lua", + ["path.win32.ffi.fs" ] = "lua/path/win32/ffi/fs.lua", + ["path.win32.ffi.types" ] = "lua/path/win32/ffi/types.lua", + ["path.win32.ffi.wcs" ] = "lua/path/win32/ffi/wcs.lua", + ["path.win32.fs" ] = "lua/path/win32/fs.lua", + ["path.win32.wcs" ] = "lua/path/win32/wcs.lua", + } +} + + + diff --git a/test/test.lua b/test/test.lua index 9c02771..6fc7237 100644 --- a/test/test.lua +++ b/test/test.lua @@ -1,864 +1,864 @@ -local lunit = require "lunit" -local TEST_CASE = lunit.TEST_CASE - -local path = require "path" - -local path_win = path.new('\\') -local path_unx = path.new('/') - -local function mkfile(P, data) - P = path.fullpath(P) - path.mkdir(path.dirname(P)) - local f, e = io.open(P, "w+b") - if not f then return nil, err end - if data then assert(f:write(data)) end - f:close() - return P -end - -local function read_file(P) - local f, err = io.open(P, "rb") - if not f then return nil, err end - local data, err = f:read("*all") - f:close() - if data then return data end - return nil, err -end - -local function up(str) - return path.IS_WINDOWS and str:upper() or str -end - -local function clone(t, o) - o = o or {} - for k,v in pairs(t) do - o[ k ] = v - end - return o -end - -local _ENV = TEST_CASE('PATH manipulation') if true then - -local function testpath(pth,p1,p2,p3) - local dir,rest = path.splitpath(pth) - local name,ext = path.splitext(rest) - assert_equal(p1, dir ) - assert_equal(p2, name) - assert_equal(p3, ext ) -end - -function test_penlight_1() - testpath ([[/bonzo/dog_stuff/cat.txt]],[[/bonzo/dog_stuff]],'cat','.txt') - testpath ([[/bonzo/dog/cat/fred.stuff]],'/bonzo/dog/cat','fred','.stuff') - testpath ([[../../alice/jones]],'../../alice','jones','') - testpath ([[alice]],'','alice','') - testpath ([[/path-to/dog/]],[[/path-to/dog]],'','') -end - -function test_penlight_2() - local p = path_unx:normalize( '/a/b' ) - assert_equal('/a/b',p) - assert_equal(p, path_unx:normalize( '/a/fred/../b' )) - assert_equal(p, path_unx:normalize( '/a//b' )) - assert_equal(p, path_unx:normalize( '/a/./b' )) - - local p = path_win:normalize( '/a/b' ) - assert_equal('\\a\\b',p) - assert_equal(p, path_win:normalize( '/a/fred/../b' )) - assert_equal(p, path_win:normalize( '/a//b' )) - assert_equal(p, path_win:normalize( '/a/./b' )) -end - -function test_penlight_3() - if not path.isdir then assert_false('lfs module not found') end - assert ( path.isdir( "../lua" )) - assert_false ( path.isfile( "../lua" )) - - assert ( path.isfile( "../lua/path.lua" ) ) - assert_false( path.isdir( "../lua/path.lua" )) -end - -function test_system() - if not path.isdir then assert_false('lfs module not found') end - if path.IS_WINDOWS then - assert_error(function()path_unx:isdir("/any/") end) - assert_pass (function()path_win:isdir("c:\\") end) - else - assert_pass (function()path_unx:isdir("/any/") end) - assert_error(function()path_win:isdir("c:\\") end) - end -end - -function test_split() - assert_equal('a', path_unx:root('/a/b/c')) - assert_equal('', path_unx:root('a/b/c')) - - assert_equal('host', path_win:root('\\\\host\\a\\b\\c')) - assert_equal('a:', path_win:root('a:\\b\\c')) - assert_equal('', path_win:root('\\b\\c')) -end - -function test_splitext() - testpath ('.log','','.log','') - testpath ('path/.log','path','.log','') - testpath ('log','','log','') - testpath ('.log/','.log','','') - testpath ('.1001.log','','.1001','.log') - - local root, ext = path.splitext(".log") - assert_equal(".log", root) - assert_equal("", ext) - assert_equal(ext, path.extension(".log")) - - root, ext = path.splitext("test/.log") - assert_equal("test/.log", root) - assert_equal("", ext) - assert_equal(ext, path.extension("test/.log")) - - root, ext = path.splitext("test/1.log") - assert_equal("test/1", root) - assert_equal(".log", ext) - assert_equal(ext, path.extension("test/1.log")) - - root, ext = path.splitext("test/.1.log") - assert_equal("test/.1", root) - assert_equal(".log", ext) - assert_equal(ext, path.extension("test/.1.log")) -end - -function test_splitdrive() - local a, b - a,b = path_unx:splitdrive('/root/etc') - assert_equal('', a) assert_equal('/root/etc', b) - - a,b = path_win:splitdrive('c:\\root\\etc') - assert_equal('c:', a) assert_equal('root\\etc', b) -end - -function test_norm() - assert_equal("..\\hello", path_win:normalize("..\\hello")) - assert_equal("..\\hello", path_win:normalize("..\\hello\\world\\..")) - assert_equal("c:\\hello", path_win:normalize("c:\\..\\hello")) - assert_equal("c:\\hello", path_win:normalize("c:\\hello\\.")) - assert_equal("c:\\hello", path_win:normalize("c:\\hello\\.\\.")) - assert_equal("\\hello", path_win:normalize("\\..\\hello")) -- full path without drive - assert_equal("\\\\host\\hello", path_win:normalize("\\\\host\\..\\hello")) - - assert_equal("/hello", path_unx:normalize("\\c\\..\\hello")) - assert_equal("../hello", path_unx:normalize("..\\hello\\world\\..")) - assert_equal("/home/test", path_unx:normalize("/home/test/.")) - assert_equal("/home/test", path_unx:normalize("/home/test/./.")) - assert_equal("/home/test/world",path_unx:normalize("/home/test/./world")) - assert_equal("/home/test", path_unx:normalize("\\home\\test\\.")) - assert_equal("/", path_unx:normalize("/")) - assert_equal("/", path_unx:normalize("/.")) - assert_equal("/", path_unx:normalize("/./.")) - assert_equal("/", path_unx:normalize("/./")) - assert_equal(".", path_unx:normalize("././")) - assert_equal("/dev", path_unx:normalize("/./dev")) -end - -function test_quote() - assert_equal('c:\\hello', path_win:quote('c:\\hello')) - assert_equal('"c:\\hello world"', path_win:quote('c:\\hello world')) - assert_equal('/hello', path_unx:quote('/hello')) - assert_equal('"/hello world"', path_unx:quote('/hello world')) - - assert_equal('c:\\hello', path_win:unquote('c:\\hello')) - assert_equal('c:\\hello', path_win:unquote('"c:\\hello"')) - assert_equal('c:\\"hello"', path_win:unquote('c:\\"hello"')) - assert_equal('c:\\hello world', path_win:unquote('"c:\\hello world"')) - assert_equal('c:\\hello world', path_win:unquote('c:\\hello world')) - assert_equal('/hello', path_unx:unquote('/hello')) - assert_equal('/hello', path_unx:unquote('"/hello"')) - assert_equal('/"hello"', path_unx:unquote('/"hello"')) - assert_equal('/hello world', path_unx:unquote('/hello world')) - assert_equal('/hello world', path_unx:unquote('"/hello world"')) -end - -function test_dir_end() - assert_equal('c:', path_win:remove_dir_end('c:\\')) - assert_equal('c:', path_win:remove_dir_end('c:\\\\')) - assert_equal('c:\\.', path_win:remove_dir_end('c:\\.\\')) - assert_equal('c:\\', path_win:ensure_dir_end('c:')) - - assert_equal('', path_unx:remove_dir_end('/')) - assert_equal('', path_unx:remove_dir_end('//')) - assert_equal('.', path_unx:remove_dir_end('./')) - assert_equal('/', path_unx:ensure_dir_end('')) - assert_equal('/', path_unx:ensure_dir_end('/')) -end - -function test_join() - assert_equal("hello", path_win:join("hello")) - assert_equal("hello\\world", path_win:join("hello", "", "world")) - assert_equal("c:\\world\\some\\path", path_win:join("hello", "", "c:\\world", "some", "path")) - assert_equal("hello\\", path_win:join("hello", "")) -end - -end - -local _ENV = TEST_CASE('PATH system error') if true then - -function test() - local p = path.IS_WINDOWS and path_unx or path_win - assert_boolean(path.IS_WINDOWS) - assert_boolean(p.IS_WINDOWS) - assert_not_equal(path.IS_WINDOWS, p.IS_WINDOWS) - assert_error(function() p:mkdir('./1') end) - assert_error(function() p:size('./1.txt') end) -end - -end - -local _ENV = TEST_CASE('PATH fullpath') if true then - -function test_user_home() - local p = assert_string(path.user_home()) - assert_equal(p, path.isdir(p)) - assert_equal(p, path.fullpath("~")) -end - -function test_win() - if path.IS_WINDOWS then - local p = assert_string(path.currentdir()) - assert_equal(p, path.isdir(p)) - local _, tp = path.splitroot(p) - assert_equal(p, path.fullpath(path.DIR_SEP .. tp)) - end -end - -end - -local _ENV = TEST_CASE('PATH make dir') if true then - -local cwd - -function teardown() - path.remove(path.join(cwd, '1', '2', '3', 'test.dat')) - path.rmdir(path.join(cwd, '1', '2', '3')) - path.rmdir(path.join(cwd, '1', '2')) - path.rmdir(path.join(cwd, '1')) -end - -function setup() - cwd = assert_string(path.currentdir()) - teardown() -end - -function test_mkdir_nested() - local DST = path.join(cwd, '1', '2', '3') - assert_equal(cwd, path.isdir(cwd)) - assert_string(path.mkdir(DST)) - assert_true (path.rmdir(DST)) -end - -function test_mkdir() - local DST = path.join(cwd, '1') - assert_equal(cwd, path.isdir(cwd)) - assert_string(path.mkdir(DST)) - assert_true (path.rmdir(DST)) -end - -function test_clean() - assert(path.isdir(cwd)) - assert(path.mkdir(path.join(cwd, '1', '2', '3'))) - assert_nil(path.rmdir(path.join(cwd, '1'))) - assert(mkfile(path.join(cwd, '1', '2', '3', 'test.dat'))) - assert_nil(path.rmdir(path.join(cwd, '1', '2', '3'))) - assert(path.remove(path.join(cwd, '1', '2', '3', 'test.dat'))) - assert(path.remove(path.join(cwd, '1', '2', '3'))) - assert_false( path.exists(path.join(cwd, '1', '2', '3')) ) -end - -end - -local _ENV = TEST_CASE('PATH findfile') if true then - -local cwd, files, dirs - -function teardown() - collectgarbage("collect") -- force clean lfs.dir - collectgarbage("collect") - path.remove(path.join(cwd, '1', '2', '3', 'test.dat')) - path.remove(path.join(cwd, '1', '2', '3', 'test.txt')) - path.remove(path.join(cwd, '1', '2', '3', 'file.dat')) - path.rmdir(path.join(cwd, '1', '2', '3')) - path.rmdir(path.join(cwd, '1', '2')) - path.rmdir(path.join(cwd, '1')) -end - -function setup() - cwd = assert_string(path.currentdir()) - teardown() - path.mkdir(path.join(cwd, '1', '2', '3')) - mkfile(path.join(cwd, '1', '2', '3', 'test.dat'), '12345') - mkfile(path.join(cwd, '1', '2', '3', 'test.txt'), '12345') - mkfile(path.join(cwd, '1', '2', '3', 'file.dat'), '12345') - - files = { - [ up(path.join(cwd, '1', '2', '3', 'test.dat')) ] = true; - [ up(path.join(cwd, '1', '2', '3', 'test.txt')) ] = true; - [ up(path.join(cwd, '1', '2', '3', 'file.dat')) ] = true; - } - - dirs = { - [ up(path.join(cwd, '1', '2', '3')) ] = true; - [ up(path.join(cwd, '1', '2')) ] = true; - [ up(path.join(cwd, '1' )) ] = true; - } -end - -function test_cwd() - assert_equal(cwd, path.fullpath(".")) -end - -function test_attr() - for P in pairs(files)do assert(path.exists(P)) end - for P in pairs(files)do assert(path.isfile(P)) end - for P in pairs(files)do assert_equal(5, path.size(P)) end - - local ts = os.time() - path.each("./1/*", function(f) - assert(path.isfile(f)) - assert(path.touch(f, ts)) - end, {skipdirs=true, recurse=true}) - - path.each("./1/*", "ft", function(f,mt) - assert_equal(ts, mt) - end, {skipdirs=true, recurse=true}) -end - -function test_findfile() - local params - - params = clone(files) - path.each("./1/2/3/*.*", function(f) - f = up(f) - assert_not_nil(params[f], "unexpected: " .. f) - params[f] = nil - end) - assert_nil(next(params)) - - params = clone(files) - for f in path.each("./1/2/3/*.*") do - f = up(f) - assert_not_nil(params[f], "unexpected: " .. f) - params[f] = nil - end - assert_nil(next(params)) - - params = clone(files) - params = clone(dirs,params) - path.each("./1/*", function(f) - f = up(f) - assert_not_nil(params[f], "unexpected: " .. f) - params[f] = nil - end, {recurse=true}) - assert_equal(up(path.join(cwd, '1' )), next(params)) - assert_nil(next(params, up(path.join(cwd, '1' )))) - - params = clone(files) - path.each("./1/2/3/*.*", "fz", function(f, sz) - f = up(f) - assert_not_nil(params[f], "unexpected: " .. f) - assert_equal(5, sz) - params[f] = nil - end) - assert_nil(next(params)) - - params = clone(files) - for f, sz in path.each("./1/2/3/*.*", "fz") do - f = up(f) - assert_not_nil(params[f], "unexpected: " .. f) - assert_equal(5, sz) - params[f] = nil - end - assert_nil(next(params)) - - params = clone(dirs) - path.each("./*", "fzm", function(f, sz, m) - f = up(f) - assert_not_nil(params[f], "unexpected: " .. f) - assert_equal('directory', m) - if path.IS_WINDOWS then assert_equal(0, sz) end - params[f] = nil - end, {skipfiles=true, recurse=true}) - assert_nil(next(params)) - -end - -function test_findfile_mask() - params = clone(files) - path.each("./1/2/3/t*.*", function(f) - f = up(f) - assert_not_nil(params[f], "unexpected: " .. f) - params[f] = nil - end) - assert_not_nil(next(params)) -end - -function test_findfile_break() - local flag = false - path.each("./1/2/3/*.*", function() - assert_false(flag) - flag = true - return 'break' - end) - assert_true(flag) -end - -end - -local _ENV = TEST_CASE('PATH rename') if true then - -local cwd - -function teardown() - path.remove(path.join(cwd, '1', 'from.dat')) - path.remove(path.join(cwd, '1', 'to.dat' )) - path.remove(path.join(cwd, '1', 'to.txt')) - path.remove(path.join(cwd, '1', 'to')) - path.remove(path.join(cwd, '1')) -end - -function setup() - cwd = assert_string(path.currentdir()) - teardown() - path.mkdir(path.join(cwd, '1')) - path.mkdir(path.join(cwd, '1', 'to')) - mkfile(path.join(cwd, '1', 'from.dat')) - mkfile(path.join(cwd, '1', 'to.dat' )) - - assert(path.isfile(path.join(cwd, '1', 'from.dat'))) - assert(path.isfile(path.join(cwd, '1', 'to.dat' ))) - assert(path.isdir (path.join(cwd, '1', 'to' ))) -end - -function test_rename_fail() - assert_nil( path.rename( - path.join(cwd, '1', 'from.dat'), - path.join(cwd, '1', 'to.dat') - )) - assert(path.exists(path.join(cwd, '1', 'from.dat'))) - assert(path.exists(path.join(cwd, '1', 'to.dat'))) - - assert_nil( path.rename( - path.join(cwd, '1', 'from.dat'), - path.join(cwd, '1', 'to') - )) - assert(path.exists(path.join(cwd, '1', 'from.dat'))) - assert(path.exists(path.join(cwd, '1', 'to'))) - - assert_nil( path.rename( - path.join(cwd, '1', 'from.txt'), - path.join(cwd, '1', 'to'), - true - )) - assert(path.exists(path.join(cwd, '1', 'from.dat'))) - assert(path.exists(path.join(cwd, '1', 'to'))) -end - -function test_rename_pass1() - assert( path.rename( - path.join(cwd, '1', 'from.dat'), - path.join(cwd, '1', 'to.txt') - )) - assert_false(path.exists(path.join(cwd, '1', 'from.dat'))) - assert(path.exists(path.join(cwd, '1', 'to.dat'))) -end - -function test_rename_force_file() - assert( path.rename( - path.join(cwd, '1', 'from.dat'), - path.join(cwd, '1', 'to.dat'), - true - )) - assert_false(path.exists(path.join(cwd, '1', 'from.dat'))) - assert(path.exists(path.join(cwd, '1', 'to.dat'))) -end - -function test_rename_force_dir() - assert_nil( path.rename( - path.join(cwd, '1', 'from.dat'), - path.join(cwd, '1', 'to'), - true - )) - assert_equal(path.join(cwd, '1', 'from.dat'), path.exists(path.join(cwd, '1', 'from.dat'))) - assert_equal(path.join(cwd, '1', 'to'), path.isdir(path.join(cwd, '1', 'to'))) -end - -end - -local _ENV = TEST_CASE('PATH chdir') if true then - -local cwd - -function teardown() - if cwd then path.chdir(cwd) end - path.rmdir(path.join(cwd, '1', '2')) - path.rmdir(path.join(cwd, '1')) -end - -function setup() - cwd = path.currentdir() - path.mkdir(path.join(cwd, '1')) - path.mkdir(path.join(cwd, '1', '2')) -end - -function test_chdir() - assert(path.isdir('./1')) - assert_false(path.exists('./2')) - assert_true(path.chdir('./1')) - assert_false(path.exists('./1')) - assert(path.isdir('./2')) -end - -end - -local _ENV = TEST_CASE('PATH copy') if true then - -local cwd, files - -function teardown() - collectgarbage("collect") -- force clean lfs.dir - collectgarbage("collect") - path.remove(path.join(cwd, '1', 'a1.txt')) - path.remove(path.join(cwd, '1', 'a2.txt')) - path.remove(path.join(cwd, '1', 'b1.txt')) - path.remove(path.join(cwd, '1', 'b2.txt')) - path.remove(path.join(cwd, '1', '2', '3', 'a1.txt')) - path.remove(path.join(cwd, '1', '2', '3', 'a2.txt')) - path.remove(path.join(cwd, '1', '2', '3', 'b1.txt')) - path.remove(path.join(cwd, '1', '2', '3', 'b2.txt')) - path.remove(path.join(cwd, '1', '2', 'a1.txt')) - path.remove(path.join(cwd, '1', '2', 'a2.txt')) - path.remove(path.join(cwd, '1', '2', 'b1.txt')) - path.remove(path.join(cwd, '1', '2', 'b2.txt')) - path.remove(path.join(cwd, '1', '2', '3')) - path.remove(path.join(cwd, '1', '2')) - path.remove(path.join(cwd, '1')) - path.remove(path.join(cwd, '2', 'to')) - path.remove(path.join(cwd, '2')) - -end - -function setup() - cwd = assert_string(path.currentdir()) - teardown() - - path.mkdir(path.join(cwd, '1')) - path.mkdir(path.join(cwd, '2', 'to')) - mkfile(path.join(cwd, '1', 'a1.txt'), '12345') - mkfile(path.join(cwd, '1', 'a2.txt'), '54321') - mkfile(path.join(cwd, '1', 'b1.txt'), '12345') - mkfile(path.join(cwd, '1', 'b2.txt'), '54321') - - files = { - [path.join(cwd, '1', 'a1.txt'):upper()] = true; - [path.join(cwd, '1', 'a2.txt'):upper()] = true; - [path.join(cwd, '1', 'b1.txt'):upper()] = true; - [path.join(cwd, '1', 'b2.txt'):upper()] = true; - } -end - -function test_copy_fail() - assert_nil( path.copy( - path.join(cwd, '1', 'a1.txt'), - path.join(cwd, '1', 'a2.txt') - )) - assert_equal("54321", read_file(path.join(cwd, '1', 'a2.txt'))) -end - -function test_copy_fail_bool() - assert_nil( path.copy( - path.join(cwd, '1', 'a1.txt'), - path.join(cwd, '1', 'a2.txt'), - false - )) - assert_equal("54321", read_file(path.join(cwd, '1', 'a2.txt'))) -end - -function test_copy_overwrite() - assert( path.copy( - path.join(cwd, '1', 'a1.txt'), - path.join(cwd, '1', 'a2.txt'), - {overwrite = true} - )) - assert_equal("12345", read_file(path.join(cwd, '1', 'a2.txt'))) -end - -function test_copy_overwrite_dir() - assert_nil( path.copy( - path.join(cwd, '1', 'a1.txt'), - path.join(cwd, '2', 'to'), - {overwrite = true} - )) - assert_equal(path.join(cwd, '2', 'to'), path.isdir(path.join(cwd, '2', 'to'))) -end - -function test_copy_overwrite_bool() - assert( path.copy( - path.join(cwd, '1', 'a1.txt'), - path.join(cwd, '1', 'a2.txt'), - true - )) - assert_equal("12345", read_file(path.join(cwd, '1', 'a2.txt'))) -end - -function test_copy_mkdir() - assert( path.copy( - path.join(cwd, '1', 'a1.txt'), - path.join(cwd, '1', '2', '3', 'a2.txt') - )) - assert_equal("12345", read_file(path.join(cwd, '1', '2', '3', 'a2.txt'))) -end - -function test_copy_batch() - assert(path.copy( - path.join(cwd, '1', 'a*.txt'), - path.join(cwd, '1', '2') - )) - assert_equal("12345", read_file(path.join(cwd, '1', '2', 'a1.txt'))) - assert_equal("54321", read_file(path.join(cwd, '1', '2', 'a2.txt'))) - assert_true(path.remove(path.join(cwd, '1', '2', 'a1.txt'))) - assert_true(path.remove(path.join(cwd, '1', '2', 'a2.txt'))) - - local fname - path.each(path.join(cwd, '1', '2', '*'), function(f) - fname = f - return true - end) - assert_nil(fname) -end - -function test_copy_accept() - local options options = { - skipdirs = true; - accept = function(src, des, opt) - local key = src:upper() - assert_true(files[key]) - assert_equal(options, opt) - files[key] = nil; - return not path.basename(src):find("^b") - end; - } - assert(path.copy( - path.join(cwd, '1', '*'), - path.join(cwd, '1', '2'), - options - )) - assert_nil(next(files)) - - assert_equal("12345", read_file(path.join(cwd, '1', '2', 'a1.txt'))) - assert_equal("54321", read_file(path.join(cwd, '1', '2', 'a2.txt'))) - assert_false(path.exists(path.join(cwd, '1', '2', '2'))) - assert_false(path.exists(path.join(cwd, '1', '2', 'b1.txt'))) - assert_false(path.exists(path.join(cwd, '1', '2', 'b2.txt'))) -end - -function test_copy_error_skip() - local ivalid_path = path.IS_WINDOWS and path.join(cwd, '1*') or "/dev/qaz" - local options options = { - error = function(err, src, des, opt) - local key = src:upper() - assert_true(files[key]) - assert_equal(options, opt) - files[key] = nil; - return true - end; - } - assert(path.copy( - path.join(cwd, '1', '*'), - ivalid_path, - options - )) - assert_nil(next(files)) -end - -function test_copy_error_break() - local ivalid_path = path.IS_WINDOWS and path.join(cwd, '1*') or "/dev/qaz" - local flag = false - assert(path.copy( - path.join(cwd, '1', '*'), - ivalid_path,{ - error = function() - assert_false(flag) - flag = true - return false - end - } - )) - assert_true(flag) -end - -end - -local _ENV = TEST_CASE('PATH clean dir') if true then - -local cwd - -function teardown() - local print = print - print = (path.remove(path.join(cwd, '1', '2', '3', 'b1.txt'))) - print = (path.remove(path.join(cwd, '1', '2', '3', 'b2.txt'))) - print = (path.remove(path.join(cwd, '1', '2', '3', 'b3.txt'))) - print = (path.remove(path.join(cwd, '1', '2', 'a1.txt'))) - print = (path.remove(path.join(cwd, '1', '2', 'a2.txt'))) - print = (path.remove(path.join(cwd, '1', '2', 'a3.txt'))) - print = (path.remove(path.join(cwd, '1', '2', '3'))) - print = (path.remove(path.join(cwd, '1', '2'))) - print = (path.remove(path.join(cwd, '1'))) -end - -function setup() - cwd = assert_string(path.currentdir()) - teardown() - mkfile(path.join(cwd, '1', '2', '3', 'b1.txt')) - mkfile(path.join(cwd, '1', '2', '3', 'b2.txt')) - mkfile(path.join(cwd, '1', '2', '3', 'b3.txt')) - mkfile(path.join(cwd, '1', '2', 'a1.txt')) - mkfile(path.join(cwd, '1', '2', 'a2.txt')) - mkfile(path.join(cwd, '1', '2', 'a3.txt')) -end - -function test_clean() - assert_equal(8, path.remove(path.join(cwd, "1", "*"), {recurse=true})) - assert_false(path.exists(path.join(cwd, "1", "2"))) -end - -function test_clean_files() - assert_equal(6, path.remove(path.join(cwd, "1", "*"), {skipdirs=true;recurse=true})) - assert(path.exists(path.join(cwd, "1", "2"))) - assert(path.exists(path.join(cwd, "1", "2", "3"))) - assert_false(path.exists(path.join(cwd, "1", "2", "3", "a1.txt"))) -end - -function test_remove() - assert_string(path.exists(path.join(cwd, "1", "2", "a1.txt"))) - assert_string(path.exists(path.join(cwd, "1", "2", "a2.txt"))) - assert_string(path.exists(path.join(cwd, "1", "2", "a3.txt"))) - assert_string(path.exists(path.join(cwd, "1", "2", "3", "b1.txt"))) - assert_string(path.exists(path.join(cwd, "1", "2", "3", "b2.txt"))) - assert_string(path.exists(path.join(cwd, "1", "2", "3", "b3.txt"))) - - assert_equal(2, path.remove(path.join(cwd, "1", "?1.txt"), {recurse=true})) - - assert_false (path.exists(path.join(cwd, "1", "2", "a1.txt"))) - assert_string(path.exists(path.join(cwd, "1", "2", "a2.txt"))) - assert_string(path.exists(path.join(cwd, "1", "2", "a3.txt"))) - assert_false (path.exists(path.join(cwd, "1", "2", "3", "b1.txt"))) - assert_string(path.exists(path.join(cwd, "1", "2", "3", "b2.txt"))) - assert_string(path.exists(path.join(cwd, "1", "2", "3", "b3.txt"))) -end - -function test_remove_accept() - local options options = { - accept = function(src, opt) - local key = src:upper() - assert_equal(options, opt) - return not not path.basename(src):find("^.[12]") - end;recurse = true; - } - assert_equal(4, path.remove(path.join(cwd, "1", "*"), options)) - - assert_false (path.exists(path.join(cwd, "1", "2", "a1.txt"))) - assert_false (path.exists(path.join(cwd, "1", "2", "a2.txt"))) - assert_string(path.exists(path.join(cwd, "1", "2", "a3.txt"))) - assert_false (path.exists(path.join(cwd, "1", "2", "3", "b1.txt"))) - assert_false (path.exists(path.join(cwd, "1", "2", "3", "b2.txt"))) - assert_string(path.exists(path.join(cwd, "1", "2", "3", "b3.txt"))) -end - -function test_remove_error_skip() - local n = 0 - assert(path.remove(path.join(cwd, '1', '*'),{ - skipdirs = true; recurse = true; - accept = function(src) - assert(path.remove(src)) - return true - end; - error = function(err, src) - n = n + 1 - return true - end; - })) - assert_equal(6, n) -end - -function test_remove_error_break() - local flag = false - assert(path.remove(path.join(cwd, '1', '*'),{ - skipdirs = true; recurse = true; - accept = function(src) - assert(path.remove(src)) - return true - end; - error = function(err, src) - assert_false(false) - flag = true - return false - end; - })) - assert_true(flag) -end - -function test_isempty() - assert_false( path.isempty(path.join(cwd, "1")) ) - assert_equal(8, path.remove(path.join(cwd, "1", "*"), {recurse=true})) - assert_equal(path.join(cwd, "1"), path.exists(path.join(cwd, "1"))) - assert_true(path.isempty(path.join(cwd, "1"))) -end - -end - -local _ENV = TEST_CASE('PATH each mask') if true then - -local cwd, J - -function teardown() - path.remove(J(cwd, '1', '2', 'a1.txt')) - path.remove(J(cwd, '1', '2', 'a2.txt')) - path.remove(J(cwd, '1', '2', 'a3.txt')) - path.remove(J(cwd, '1', '2')) - path.remove(J(cwd, '1')) -end - -function setup() - J = path.join - cwd = assert_string(path.currentdir()) - teardown() - mkfile(J(cwd, '1', '2', 'a1.txt')) - mkfile(J(cwd, '1', '2', 'a2.txt')) - mkfile(J(cwd, '1', '2', 'a3.txt')) -end - -function test_no_mask1() - local mask = path.ensure_dir_end(J(cwd, '1', '2')) - local files = { - [ J(cwd, '1', '2', 'a1.txt') ] = true; - [ J(cwd, '1', '2', 'a2.txt') ] = true; - [ J(cwd, '1', '2', 'a3.txt') ] = true; - } - path.each(mask, function(f) - assert_true(files[f], "unexpected: " .. f) - files[f] = nil - end) - assert_nil(next(files)) -end - -function test_no_mask2() - local mask = J(cwd, '1', '2') - local files = { - [ J(cwd, '1', '2') ] = true; - } - path.each(mask, function(f) - assert_true(files[f], "unexpected: " .. f) - files[f] = nil - end) - assert_nil(next(files)) -end - -end - +local lunit = require "lunit" +local TEST_CASE = lunit.TEST_CASE + +local path = require "path" + +local path_win = path.new('\\') +local path_unx = path.new('/') + +local function mkfile(P, data) + P = path.fullpath(P) + path.mkdir(path.dirname(P)) + local f, e = io.open(P, "w+b") + if not f then return nil, err end + if data then assert(f:write(data)) end + f:close() + return P +end + +local function read_file(P) + local f, err = io.open(P, "rb") + if not f then return nil, err end + local data, err = f:read("*all") + f:close() + if data then return data end + return nil, err +end + +local function up(str) + return path.IS_WINDOWS and str:upper() or str +end + +local function clone(t, o) + o = o or {} + for k,v in pairs(t) do + o[ k ] = v + end + return o +end + +local _ENV = TEST_CASE('PATH manipulation') if true then + +local function testpath(pth,p1,p2,p3) + local dir,rest = path.splitpath(pth) + local name,ext = path.splitext(rest) + assert_equal(p1, dir ) + assert_equal(p2, name) + assert_equal(p3, ext ) +end + +function test_penlight_1() + testpath ([[/bonzo/dog_stuff/cat.txt]],[[/bonzo/dog_stuff]],'cat','.txt') + testpath ([[/bonzo/dog/cat/fred.stuff]],'/bonzo/dog/cat','fred','.stuff') + testpath ([[../../alice/jones]],'../../alice','jones','') + testpath ([[alice]],'','alice','') + testpath ([[/path-to/dog/]],[[/path-to/dog]],'','') +end + +function test_penlight_2() + local p = path_unx:normalize( '/a/b' ) + assert_equal('/a/b',p) + assert_equal(p, path_unx:normalize( '/a/fred/../b' )) + assert_equal(p, path_unx:normalize( '/a//b' )) + assert_equal(p, path_unx:normalize( '/a/./b' )) + + local p = path_win:normalize( '/a/b' ) + assert_equal('\\a\\b',p) + assert_equal(p, path_win:normalize( '/a/fred/../b' )) + assert_equal(p, path_win:normalize( '/a//b' )) + assert_equal(p, path_win:normalize( '/a/./b' )) +end + +function test_penlight_3() + if not path.isdir then assert_false('lfs module not found') end + assert ( path.isdir( "../lua" )) + assert_false ( path.isfile( "../lua" )) + + assert ( path.isfile( "../lua/path.lua" ) ) + assert_false( path.isdir( "../lua/path.lua" )) +end + +function test_system() + if not path.isdir then assert_false('lfs module not found') end + if path.IS_WINDOWS then + assert_error(function()path_unx:isdir("/any/") end) + assert_pass (function()path_win:isdir("c:\\") end) + else + assert_pass (function()path_unx:isdir("/any/") end) + assert_error(function()path_win:isdir("c:\\") end) + end +end + +function test_split() + assert_equal('a', path_unx:root('/a/b/c')) + assert_equal('', path_unx:root('a/b/c')) + + assert_equal('host', path_win:root('\\\\host\\a\\b\\c')) + assert_equal('a:', path_win:root('a:\\b\\c')) + assert_equal('', path_win:root('\\b\\c')) +end + +function test_splitext() + testpath ('.log','','.log','') + testpath ('path/.log','path','.log','') + testpath ('log','','log','') + testpath ('.log/','.log','','') + testpath ('.1001.log','','.1001','.log') + + local root, ext = path.splitext(".log") + assert_equal(".log", root) + assert_equal("", ext) + assert_equal(ext, path.extension(".log")) + + root, ext = path.splitext("test/.log") + assert_equal("test/.log", root) + assert_equal("", ext) + assert_equal(ext, path.extension("test/.log")) + + root, ext = path.splitext("test/1.log") + assert_equal("test/1", root) + assert_equal(".log", ext) + assert_equal(ext, path.extension("test/1.log")) + + root, ext = path.splitext("test/.1.log") + assert_equal("test/.1", root) + assert_equal(".log", ext) + assert_equal(ext, path.extension("test/.1.log")) +end + +function test_splitdrive() + local a, b + a,b = path_unx:splitdrive('/root/etc') + assert_equal('', a) assert_equal('/root/etc', b) + + a,b = path_win:splitdrive('c:\\root\\etc') + assert_equal('c:', a) assert_equal('root\\etc', b) +end + +function test_norm() + assert_equal("..\\hello", path_win:normalize("..\\hello")) + assert_equal("..\\hello", path_win:normalize("..\\hello\\world\\..")) + assert_equal("c:\\hello", path_win:normalize("c:\\..\\hello")) + assert_equal("c:\\hello", path_win:normalize("c:\\hello\\.")) + assert_equal("c:\\hello", path_win:normalize("c:\\hello\\.\\.")) + assert_equal("\\hello", path_win:normalize("\\..\\hello")) -- full path without drive + assert_equal("\\\\host\\hello", path_win:normalize("\\\\host\\..\\hello")) + + assert_equal("/hello", path_unx:normalize("\\c\\..\\hello")) + assert_equal("../hello", path_unx:normalize("..\\hello\\world\\..")) + assert_equal("/home/test", path_unx:normalize("/home/test/.")) + assert_equal("/home/test", path_unx:normalize("/home/test/./.")) + assert_equal("/home/test/world",path_unx:normalize("/home/test/./world")) + assert_equal("/home/test", path_unx:normalize("\\home\\test\\.")) + assert_equal("/", path_unx:normalize("/")) + assert_equal("/", path_unx:normalize("/.")) + assert_equal("/", path_unx:normalize("/./.")) + assert_equal("/", path_unx:normalize("/./")) + assert_equal(".", path_unx:normalize("././")) + assert_equal("/dev", path_unx:normalize("/./dev")) +end + +function test_quote() + assert_equal('c:\\hello', path_win:quote('c:\\hello')) + assert_equal('"c:\\hello world"', path_win:quote('c:\\hello world')) + assert_equal('/hello', path_unx:quote('/hello')) + assert_equal('"/hello world"', path_unx:quote('/hello world')) + + assert_equal('c:\\hello', path_win:unquote('c:\\hello')) + assert_equal('c:\\hello', path_win:unquote('"c:\\hello"')) + assert_equal('c:\\"hello"', path_win:unquote('c:\\"hello"')) + assert_equal('c:\\hello world', path_win:unquote('"c:\\hello world"')) + assert_equal('c:\\hello world', path_win:unquote('c:\\hello world')) + assert_equal('/hello', path_unx:unquote('/hello')) + assert_equal('/hello', path_unx:unquote('"/hello"')) + assert_equal('/"hello"', path_unx:unquote('/"hello"')) + assert_equal('/hello world', path_unx:unquote('/hello world')) + assert_equal('/hello world', path_unx:unquote('"/hello world"')) +end + +function test_dir_end() + assert_equal('c:', path_win:remove_dir_end('c:\\')) + assert_equal('c:', path_win:remove_dir_end('c:\\\\')) + assert_equal('c:\\.', path_win:remove_dir_end('c:\\.\\')) + assert_equal('c:\\', path_win:ensure_dir_end('c:')) + + assert_equal('', path_unx:remove_dir_end('/')) + assert_equal('', path_unx:remove_dir_end('//')) + assert_equal('.', path_unx:remove_dir_end('./')) + assert_equal('/', path_unx:ensure_dir_end('')) + assert_equal('/', path_unx:ensure_dir_end('/')) +end + +function test_join() + assert_equal("hello", path_win:join("hello")) + assert_equal("hello\\world", path_win:join("hello", "", "world")) + assert_equal("c:\\world\\some\\path", path_win:join("hello", "", "c:\\world", "some", "path")) + assert_equal("hello\\", path_win:join("hello", "")) +end + +end + +local _ENV = TEST_CASE('PATH system error') if true then + +function test() + local p = path.IS_WINDOWS and path_unx or path_win + assert_boolean(path.IS_WINDOWS) + assert_boolean(p.IS_WINDOWS) + assert_not_equal(path.IS_WINDOWS, p.IS_WINDOWS) + assert_error(function() p:mkdir('./1') end) + assert_error(function() p:size('./1.txt') end) +end + +end + +local _ENV = TEST_CASE('PATH fullpath') if true then + +function test_user_home() + local p = assert_string(path.user_home()) + assert_equal(p, path.isdir(p)) + assert_equal(p, path.fullpath("~")) +end + +function test_win() + if path.IS_WINDOWS then + local p = assert_string(path.currentdir()) + assert_equal(p, path.isdir(p)) + local _, tp = path.splitroot(p) + assert_equal(p, path.fullpath(path.DIR_SEP .. tp)) + end +end + +end + +local _ENV = TEST_CASE('PATH make dir') if true then + +local cwd + +function teardown() + path.remove(path.join(cwd, '1', '2', '3', 'test.dat')) + path.rmdir(path.join(cwd, '1', '2', '3')) + path.rmdir(path.join(cwd, '1', '2')) + path.rmdir(path.join(cwd, '1')) +end + +function setup() + cwd = assert_string(path.currentdir()) + teardown() +end + +function test_mkdir_nested() + local DST = path.join(cwd, '1', '2', '3') + assert_equal(cwd, path.isdir(cwd)) + assert_string(path.mkdir(DST)) + assert_true (path.rmdir(DST)) +end + +function test_mkdir() + local DST = path.join(cwd, '1') + assert_equal(cwd, path.isdir(cwd)) + assert_string(path.mkdir(DST)) + assert_true (path.rmdir(DST)) +end + +function test_clean() + assert(path.isdir(cwd)) + assert(path.mkdir(path.join(cwd, '1', '2', '3'))) + assert_nil(path.rmdir(path.join(cwd, '1'))) + assert(mkfile(path.join(cwd, '1', '2', '3', 'test.dat'))) + assert_nil(path.rmdir(path.join(cwd, '1', '2', '3'))) + assert(path.remove(path.join(cwd, '1', '2', '3', 'test.dat'))) + assert(path.remove(path.join(cwd, '1', '2', '3'))) + assert_false( path.exists(path.join(cwd, '1', '2', '3')) ) +end + +end + +local _ENV = TEST_CASE('PATH findfile') if true then + +local cwd, files, dirs + +function teardown() + collectgarbage("collect") -- force clean lfs.dir + collectgarbage("collect") + path.remove(path.join(cwd, '1', '2', '3', 'test.dat')) + path.remove(path.join(cwd, '1', '2', '3', 'test.txt')) + path.remove(path.join(cwd, '1', '2', '3', 'file.dat')) + path.rmdir(path.join(cwd, '1', '2', '3')) + path.rmdir(path.join(cwd, '1', '2')) + path.rmdir(path.join(cwd, '1')) +end + +function setup() + cwd = assert_string(path.currentdir()) + teardown() + path.mkdir(path.join(cwd, '1', '2', '3')) + mkfile(path.join(cwd, '1', '2', '3', 'test.dat'), '12345') + mkfile(path.join(cwd, '1', '2', '3', 'test.txt'), '12345') + mkfile(path.join(cwd, '1', '2', '3', 'file.dat'), '12345') + + files = { + [ up(path.join(cwd, '1', '2', '3', 'test.dat')) ] = true; + [ up(path.join(cwd, '1', '2', '3', 'test.txt')) ] = true; + [ up(path.join(cwd, '1', '2', '3', 'file.dat')) ] = true; + } + + dirs = { + [ up(path.join(cwd, '1', '2', '3')) ] = true; + [ up(path.join(cwd, '1', '2')) ] = true; + [ up(path.join(cwd, '1' )) ] = true; + } +end + +function test_cwd() + assert_equal(cwd, path.fullpath(".")) +end + +function test_attr() + for P in pairs(files)do assert(path.exists(P)) end + for P in pairs(files)do assert(path.isfile(P)) end + for P in pairs(files)do assert_equal(5, path.size(P)) end + + local ts = os.time() + path.each("./1/*", function(f) + assert(path.isfile(f)) + assert(path.touch(f, ts)) + end, {skipdirs=true, recurse=true}) + + path.each("./1/*", "ft", function(f,mt) + assert_equal(ts, mt) + end, {skipdirs=true, recurse=true}) +end + +function test_findfile() + local params + + params = clone(files) + path.each("./1/2/3/*.*", function(f) + f = up(f) + assert_not_nil(params[f], "unexpected: " .. f) + params[f] = nil + end) + assert_nil(next(params)) + + params = clone(files) + for f in path.each("./1/2/3/*.*") do + f = up(f) + assert_not_nil(params[f], "unexpected: " .. f) + params[f] = nil + end + assert_nil(next(params)) + + params = clone(files) + params = clone(dirs,params) + path.each("./1/*", function(f) + f = up(f) + assert_not_nil(params[f], "unexpected: " .. f) + params[f] = nil + end, {recurse=true}) + assert_equal(up(path.join(cwd, '1' )), next(params)) + assert_nil(next(params, up(path.join(cwd, '1' )))) + + params = clone(files) + path.each("./1/2/3/*.*", "fz", function(f, sz) + f = up(f) + assert_not_nil(params[f], "unexpected: " .. f) + assert_equal(5, sz) + params[f] = nil + end) + assert_nil(next(params)) + + params = clone(files) + for f, sz in path.each("./1/2/3/*.*", "fz") do + f = up(f) + assert_not_nil(params[f], "unexpected: " .. f) + assert_equal(5, sz) + params[f] = nil + end + assert_nil(next(params)) + + params = clone(dirs) + path.each("./*", "fzm", function(f, sz, m) + f = up(f) + assert_not_nil(params[f], "unexpected: " .. f) + assert_equal('directory', m) + if path.IS_WINDOWS then assert_equal(0, sz) end + params[f] = nil + end, {skipfiles=true, recurse=true}) + assert_nil(next(params)) + +end + +function test_findfile_mask() + params = clone(files) + path.each("./1/2/3/t*.*", function(f) + f = up(f) + assert_not_nil(params[f], "unexpected: " .. f) + params[f] = nil + end) + assert_not_nil(next(params)) +end + +function test_findfile_break() + local flag = false + path.each("./1/2/3/*.*", function() + assert_false(flag) + flag = true + return 'break' + end) + assert_true(flag) +end + +end + +local _ENV = TEST_CASE('PATH rename') if true then + +local cwd + +function teardown() + path.remove(path.join(cwd, '1', 'from.dat')) + path.remove(path.join(cwd, '1', 'to.dat' )) + path.remove(path.join(cwd, '1', 'to.txt')) + path.remove(path.join(cwd, '1', 'to')) + path.remove(path.join(cwd, '1')) +end + +function setup() + cwd = assert_string(path.currentdir()) + teardown() + path.mkdir(path.join(cwd, '1')) + path.mkdir(path.join(cwd, '1', 'to')) + mkfile(path.join(cwd, '1', 'from.dat')) + mkfile(path.join(cwd, '1', 'to.dat' )) + + assert(path.isfile(path.join(cwd, '1', 'from.dat'))) + assert(path.isfile(path.join(cwd, '1', 'to.dat' ))) + assert(path.isdir (path.join(cwd, '1', 'to' ))) +end + +function test_rename_fail() + assert_nil( path.rename( + path.join(cwd, '1', 'from.dat'), + path.join(cwd, '1', 'to.dat') + )) + assert(path.exists(path.join(cwd, '1', 'from.dat'))) + assert(path.exists(path.join(cwd, '1', 'to.dat'))) + + assert_nil( path.rename( + path.join(cwd, '1', 'from.dat'), + path.join(cwd, '1', 'to') + )) + assert(path.exists(path.join(cwd, '1', 'from.dat'))) + assert(path.exists(path.join(cwd, '1', 'to'))) + + assert_nil( path.rename( + path.join(cwd, '1', 'from.txt'), + path.join(cwd, '1', 'to'), + true + )) + assert(path.exists(path.join(cwd, '1', 'from.dat'))) + assert(path.exists(path.join(cwd, '1', 'to'))) +end + +function test_rename_pass1() + assert( path.rename( + path.join(cwd, '1', 'from.dat'), + path.join(cwd, '1', 'to.txt') + )) + assert_false(path.exists(path.join(cwd, '1', 'from.dat'))) + assert(path.exists(path.join(cwd, '1', 'to.dat'))) +end + +function test_rename_force_file() + assert( path.rename( + path.join(cwd, '1', 'from.dat'), + path.join(cwd, '1', 'to.dat'), + true + )) + assert_false(path.exists(path.join(cwd, '1', 'from.dat'))) + assert(path.exists(path.join(cwd, '1', 'to.dat'))) +end + +function test_rename_force_dir() + assert_nil( path.rename( + path.join(cwd, '1', 'from.dat'), + path.join(cwd, '1', 'to'), + true + )) + assert_equal(path.join(cwd, '1', 'from.dat'), path.exists(path.join(cwd, '1', 'from.dat'))) + assert_equal(path.join(cwd, '1', 'to'), path.isdir(path.join(cwd, '1', 'to'))) +end + +end + +local _ENV = TEST_CASE('PATH chdir') if true then + +local cwd + +function teardown() + if cwd then path.chdir(cwd) end + path.rmdir(path.join(cwd, '1', '2')) + path.rmdir(path.join(cwd, '1')) +end + +function setup() + cwd = path.currentdir() + path.mkdir(path.join(cwd, '1')) + path.mkdir(path.join(cwd, '1', '2')) +end + +function test_chdir() + assert(path.isdir('./1')) + assert_false(path.exists('./2')) + assert_true(path.chdir('./1')) + assert_false(path.exists('./1')) + assert(path.isdir('./2')) +end + +end + +local _ENV = TEST_CASE('PATH copy') if true then + +local cwd, files + +function teardown() + collectgarbage("collect") -- force clean lfs.dir + collectgarbage("collect") + path.remove(path.join(cwd, '1', 'a1.txt')) + path.remove(path.join(cwd, '1', 'a2.txt')) + path.remove(path.join(cwd, '1', 'b1.txt')) + path.remove(path.join(cwd, '1', 'b2.txt')) + path.remove(path.join(cwd, '1', '2', '3', 'a1.txt')) + path.remove(path.join(cwd, '1', '2', '3', 'a2.txt')) + path.remove(path.join(cwd, '1', '2', '3', 'b1.txt')) + path.remove(path.join(cwd, '1', '2', '3', 'b2.txt')) + path.remove(path.join(cwd, '1', '2', 'a1.txt')) + path.remove(path.join(cwd, '1', '2', 'a2.txt')) + path.remove(path.join(cwd, '1', '2', 'b1.txt')) + path.remove(path.join(cwd, '1', '2', 'b2.txt')) + path.remove(path.join(cwd, '1', '2', '3')) + path.remove(path.join(cwd, '1', '2')) + path.remove(path.join(cwd, '1')) + path.remove(path.join(cwd, '2', 'to')) + path.remove(path.join(cwd, '2')) + +end + +function setup() + cwd = assert_string(path.currentdir()) + teardown() + + path.mkdir(path.join(cwd, '1')) + path.mkdir(path.join(cwd, '2', 'to')) + mkfile(path.join(cwd, '1', 'a1.txt'), '12345') + mkfile(path.join(cwd, '1', 'a2.txt'), '54321') + mkfile(path.join(cwd, '1', 'b1.txt'), '12345') + mkfile(path.join(cwd, '1', 'b2.txt'), '54321') + + files = { + [path.join(cwd, '1', 'a1.txt'):upper()] = true; + [path.join(cwd, '1', 'a2.txt'):upper()] = true; + [path.join(cwd, '1', 'b1.txt'):upper()] = true; + [path.join(cwd, '1', 'b2.txt'):upper()] = true; + } +end + +function test_copy_fail() + assert_nil( path.copy( + path.join(cwd, '1', 'a1.txt'), + path.join(cwd, '1', 'a2.txt') + )) + assert_equal("54321", read_file(path.join(cwd, '1', 'a2.txt'))) +end + +function test_copy_fail_bool() + assert_nil( path.copy( + path.join(cwd, '1', 'a1.txt'), + path.join(cwd, '1', 'a2.txt'), + false + )) + assert_equal("54321", read_file(path.join(cwd, '1', 'a2.txt'))) +end + +function test_copy_overwrite() + assert( path.copy( + path.join(cwd, '1', 'a1.txt'), + path.join(cwd, '1', 'a2.txt'), + {overwrite = true} + )) + assert_equal("12345", read_file(path.join(cwd, '1', 'a2.txt'))) +end + +function test_copy_overwrite_dir() + assert_nil( path.copy( + path.join(cwd, '1', 'a1.txt'), + path.join(cwd, '2', 'to'), + {overwrite = true} + )) + assert_equal(path.join(cwd, '2', 'to'), path.isdir(path.join(cwd, '2', 'to'))) +end + +function test_copy_overwrite_bool() + assert( path.copy( + path.join(cwd, '1', 'a1.txt'), + path.join(cwd, '1', 'a2.txt'), + true + )) + assert_equal("12345", read_file(path.join(cwd, '1', 'a2.txt'))) +end + +function test_copy_mkdir() + assert( path.copy( + path.join(cwd, '1', 'a1.txt'), + path.join(cwd, '1', '2', '3', 'a2.txt') + )) + assert_equal("12345", read_file(path.join(cwd, '1', '2', '3', 'a2.txt'))) +end + +function test_copy_batch() + assert(path.copy( + path.join(cwd, '1', 'a*.txt'), + path.join(cwd, '1', '2') + )) + assert_equal("12345", read_file(path.join(cwd, '1', '2', 'a1.txt'))) + assert_equal("54321", read_file(path.join(cwd, '1', '2', 'a2.txt'))) + assert_true(path.remove(path.join(cwd, '1', '2', 'a1.txt'))) + assert_true(path.remove(path.join(cwd, '1', '2', 'a2.txt'))) + + local fname + path.each(path.join(cwd, '1', '2', '*'), function(f) + fname = f + return true + end) + assert_nil(fname) +end + +function test_copy_accept() + local options options = { + skipdirs = true; + accept = function(src, des, opt) + local key = src:upper() + assert_true(files[key]) + assert_equal(options, opt) + files[key] = nil; + return not path.basename(src):find("^b") + end; + } + assert(path.copy( + path.join(cwd, '1', '*'), + path.join(cwd, '1', '2'), + options + )) + assert_nil(next(files)) + + assert_equal("12345", read_file(path.join(cwd, '1', '2', 'a1.txt'))) + assert_equal("54321", read_file(path.join(cwd, '1', '2', 'a2.txt'))) + assert_false(path.exists(path.join(cwd, '1', '2', '2'))) + assert_false(path.exists(path.join(cwd, '1', '2', 'b1.txt'))) + assert_false(path.exists(path.join(cwd, '1', '2', 'b2.txt'))) +end + +function test_copy_error_skip() + local ivalid_path = path.IS_WINDOWS and path.join(cwd, '1*') or "/dev/qaz" + local options options = { + error = function(err, src, des, opt) + local key = src:upper() + assert_true(files[key]) + assert_equal(options, opt) + files[key] = nil; + return true + end; + } + assert(path.copy( + path.join(cwd, '1', '*'), + ivalid_path, + options + )) + assert_nil(next(files)) +end + +function test_copy_error_break() + local ivalid_path = path.IS_WINDOWS and path.join(cwd, '1*') or "/dev/qaz" + local flag = false + assert(path.copy( + path.join(cwd, '1', '*'), + ivalid_path,{ + error = function() + assert_false(flag) + flag = true + return false + end + } + )) + assert_true(flag) +end + +end + +local _ENV = TEST_CASE('PATH clean dir') if true then + +local cwd + +function teardown() + local print = print + print = (path.remove(path.join(cwd, '1', '2', '3', 'b1.txt'))) + print = (path.remove(path.join(cwd, '1', '2', '3', 'b2.txt'))) + print = (path.remove(path.join(cwd, '1', '2', '3', 'b3.txt'))) + print = (path.remove(path.join(cwd, '1', '2', 'a1.txt'))) + print = (path.remove(path.join(cwd, '1', '2', 'a2.txt'))) + print = (path.remove(path.join(cwd, '1', '2', 'a3.txt'))) + print = (path.remove(path.join(cwd, '1', '2', '3'))) + print = (path.remove(path.join(cwd, '1', '2'))) + print = (path.remove(path.join(cwd, '1'))) +end + +function setup() + cwd = assert_string(path.currentdir()) + teardown() + mkfile(path.join(cwd, '1', '2', '3', 'b1.txt')) + mkfile(path.join(cwd, '1', '2', '3', 'b2.txt')) + mkfile(path.join(cwd, '1', '2', '3', 'b3.txt')) + mkfile(path.join(cwd, '1', '2', 'a1.txt')) + mkfile(path.join(cwd, '1', '2', 'a2.txt')) + mkfile(path.join(cwd, '1', '2', 'a3.txt')) +end + +function test_clean() + assert_equal(8, path.remove(path.join(cwd, "1", "*"), {recurse=true})) + assert_false(path.exists(path.join(cwd, "1", "2"))) +end + +function test_clean_files() + assert_equal(6, path.remove(path.join(cwd, "1", "*"), {skipdirs=true;recurse=true})) + assert(path.exists(path.join(cwd, "1", "2"))) + assert(path.exists(path.join(cwd, "1", "2", "3"))) + assert_false(path.exists(path.join(cwd, "1", "2", "3", "a1.txt"))) +end + +function test_remove() + assert_string(path.exists(path.join(cwd, "1", "2", "a1.txt"))) + assert_string(path.exists(path.join(cwd, "1", "2", "a2.txt"))) + assert_string(path.exists(path.join(cwd, "1", "2", "a3.txt"))) + assert_string(path.exists(path.join(cwd, "1", "2", "3", "b1.txt"))) + assert_string(path.exists(path.join(cwd, "1", "2", "3", "b2.txt"))) + assert_string(path.exists(path.join(cwd, "1", "2", "3", "b3.txt"))) + + assert_equal(2, path.remove(path.join(cwd, "1", "?1.txt"), {recurse=true})) + + assert_false (path.exists(path.join(cwd, "1", "2", "a1.txt"))) + assert_string(path.exists(path.join(cwd, "1", "2", "a2.txt"))) + assert_string(path.exists(path.join(cwd, "1", "2", "a3.txt"))) + assert_false (path.exists(path.join(cwd, "1", "2", "3", "b1.txt"))) + assert_string(path.exists(path.join(cwd, "1", "2", "3", "b2.txt"))) + assert_string(path.exists(path.join(cwd, "1", "2", "3", "b3.txt"))) +end + +function test_remove_accept() + local options options = { + accept = function(src, opt) + local key = src:upper() + assert_equal(options, opt) + return not not path.basename(src):find("^.[12]") + end;recurse = true; + } + assert_equal(4, path.remove(path.join(cwd, "1", "*"), options)) + + assert_false (path.exists(path.join(cwd, "1", "2", "a1.txt"))) + assert_false (path.exists(path.join(cwd, "1", "2", "a2.txt"))) + assert_string(path.exists(path.join(cwd, "1", "2", "a3.txt"))) + assert_false (path.exists(path.join(cwd, "1", "2", "3", "b1.txt"))) + assert_false (path.exists(path.join(cwd, "1", "2", "3", "b2.txt"))) + assert_string(path.exists(path.join(cwd, "1", "2", "3", "b3.txt"))) +end + +function test_remove_error_skip() + local n = 0 + assert(path.remove(path.join(cwd, '1', '*'),{ + skipdirs = true; recurse = true; + accept = function(src) + assert(path.remove(src)) + return true + end; + error = function(err, src) + n = n + 1 + return true + end; + })) + assert_equal(6, n) +end + +function test_remove_error_break() + local flag = false + assert(path.remove(path.join(cwd, '1', '*'),{ + skipdirs = true; recurse = true; + accept = function(src) + assert(path.remove(src)) + return true + end; + error = function(err, src) + assert_false(false) + flag = true + return false + end; + })) + assert_true(flag) +end + +function test_isempty() + assert_false( path.isempty(path.join(cwd, "1")) ) + assert_equal(8, path.remove(path.join(cwd, "1", "*"), {recurse=true})) + assert_equal(path.join(cwd, "1"), path.exists(path.join(cwd, "1"))) + assert_true(path.isempty(path.join(cwd, "1"))) +end + +end + +local _ENV = TEST_CASE('PATH each mask') if true then + +local cwd, J + +function teardown() + path.remove(J(cwd, '1', '2', 'a1.txt')) + path.remove(J(cwd, '1', '2', 'a2.txt')) + path.remove(J(cwd, '1', '2', 'a3.txt')) + path.remove(J(cwd, '1', '2')) + path.remove(J(cwd, '1')) +end + +function setup() + J = path.join + cwd = assert_string(path.currentdir()) + teardown() + mkfile(J(cwd, '1', '2', 'a1.txt')) + mkfile(J(cwd, '1', '2', 'a2.txt')) + mkfile(J(cwd, '1', '2', 'a3.txt')) +end + +function test_no_mask1() + local mask = path.ensure_dir_end(J(cwd, '1', '2')) + local files = { + [ J(cwd, '1', '2', 'a1.txt') ] = true; + [ J(cwd, '1', '2', 'a2.txt') ] = true; + [ J(cwd, '1', '2', 'a3.txt') ] = true; + } + path.each(mask, function(f) + assert_true(files[f], "unexpected: " .. f) + files[f] = nil + end) + assert_nil(next(files)) +end + +function test_no_mask2() + local mask = J(cwd, '1', '2') + local files = { + [ J(cwd, '1', '2') ] = true; + } + path.each(mask, function(f) + assert_true(files[f], "unexpected: " .. f) + files[f] = nil + end) + assert_nil(next(files)) +end + +end + if not LUNIT_RUN then lunit.run() end \ No newline at end of file