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