├── test ├── test_wcs.lua ├── report_coverage.lua ├── utils.lua ├── run.lua ├── .luacov ├── each_perf.lua ├── test_each.lua ├── test_lfs.lua └── test.lua ├── doc ├── config.ld ├── fs.ldoc └── path.ldoc ├── dist.info ├── lua ├── path │ ├── lfs │ │ ├── fs.lua │ │ └── impl │ │ │ └── fs.lua │ ├── syscall │ │ └── fs.lua │ ├── module.lua │ ├── fs.lua │ ├── win32 │ │ ├── wcs.lua │ │ ├── alien │ │ │ ├── types.lua │ │ │ ├── wcs.lua │ │ │ ├── utils.lua │ │ │ └── fs.lua │ │ └── ffi │ │ │ ├── wcs.lua │ │ │ ├── types.lua │ │ │ └── fs.lua │ └── findfile.lua └── path.lua ├── examples ├── walk_empty_dirs.lua └── walk_old_files.lua ├── lakefile ├── LICENCE.txt ├── CMakeLists.txt ├── rockspecs ├── lua-path-0.1.0-1.rockspec ├── lua-path-0.1.1-1.rockspec ├── lua-path-0.2.0-1.rockspec ├── lua-path-0.2.3-1.rockspec ├── lua-path-0.2.4-1.rockspec ├── lua-path-0.3.0-1.rockspec ├── lua-path-0.2.1-1.rockspec ├── lua-path-0.2.2-1.rockspec ├── lua-path-0.3.0-2.rockspec ├── lua-path-0.3.1-1.rockspec ├── lua-path-0.3.1-2.rockspec └── lua-path-scm-0.rockspec ├── README.md ├── appveyor.yml ├── .travis.yml └── cmake ├── FindLua.cmake ├── lua.cmake └── dist.cmake /test/test_wcs.lua: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moteus/lua-path/HEAD/test/test_wcs.lua -------------------------------------------------------------------------------- /doc/config.ld: -------------------------------------------------------------------------------- 1 | format='markdown' 2 | file = { 3 | 'path.ldoc', 4 | 'fs.ldoc', 5 | } 6 | -------------------------------------------------------------------------------- /test/report_coverage.lua: -------------------------------------------------------------------------------- 1 | local reporter = require "luacov.reporter.coveralls" 2 | return reporter.report() 3 | -------------------------------------------------------------------------------- /dist.info: -------------------------------------------------------------------------------- 1 | name = "lua-path" 2 | version = "0.3.1" 3 | 4 | desc = "File system path manipulation library" 5 | author = "Alexey Melnichuk" 6 | license = "MIT/X11" 7 | url = "https://github.com/moteus/lua-path" 8 | maintainer = "Alexey Melnichuk" 9 | 10 | depends = { 11 | "lua >= 5.1" 12 | } 13 | -------------------------------------------------------------------------------- /test/utils.lua: -------------------------------------------------------------------------------- 1 | local lunit = require "lunit" 2 | local skip = function (msg) return function() lunit.fail("#SKIP: " .. msg) end end 3 | local IS_LUA52 = _VERSION >= 'Lua 5.2' 4 | 5 | local TEST_CASE = function (name) 6 | if not IS_LUA52 then 7 | module(name, package.seeall, lunit.testcase) 8 | setfenv(2, _M) 9 | else 10 | return lunit.module(name, 'seeall') 11 | end 12 | end 13 | 14 | return { 15 | TEST_CASE = TEST_CASE; 16 | skip = skip; 17 | } -------------------------------------------------------------------------------- /lua/path/lfs/fs.lua: -------------------------------------------------------------------------------- 1 | ------------------------------------------------------------------ 2 | -- 3 | -- Author: Alexey Melnichuk 4 | -- 5 | -- Copyright (C) 2013-2016 Alexey Melnichuk 6 | -- 7 | -- Licensed according to the included 'LICENCE' document 8 | -- 9 | -- This file is part of lua-path library. 10 | -- 11 | ------------------------------------------------------------------ 12 | 13 | local lfs = require"lfs" 14 | return require"path.lfs.impl.fs"(lfs) 15 | -------------------------------------------------------------------------------- /examples/walk_empty_dirs.lua: -------------------------------------------------------------------------------- 1 | local path = require "path" 2 | 3 | local function is_empty_dir(p) 4 | local ok = path.each(path.join(p, '*.*'), function() 5 | return true 6 | end) 7 | return not ok 8 | end 9 | 10 | local function walk_empty_dirs(p, fn) 11 | local is_recurse = p:sub(1,1) == '!' 12 | if is_recurse then p = p:sub(2) end 13 | 14 | path.each(path.join(p, '*'), fn, { 15 | skipfiles=true, skipdirs=false, recurse=is_recurse, filter=is_empty_dir, 16 | }) 17 | end 18 | 19 | return walk_empty_dirs -------------------------------------------------------------------------------- /lua/path/syscall/fs.lua: -------------------------------------------------------------------------------- 1 | ------------------------------------------------------------------ 2 | -- 3 | -- Author: Alexey Melnichuk 4 | -- 5 | -- Copyright (C) 2013-2016 Alexey Melnichuk 6 | -- 7 | -- Licensed according to the included 'LICENCE' document 8 | -- 9 | -- This file is part of lua-path library. 10 | -- 11 | ------------------------------------------------------------------ 12 | 13 | local lfs = require"syscall.lfs" 14 | return require"path.lfs.impl.fs"(lfs) 15 | -------------------------------------------------------------------------------- /lua/path/module.lua: -------------------------------------------------------------------------------- 1 | ------------------------------------------------------------------ 2 | -- 3 | -- Author: Alexey Melnichuk 4 | -- 5 | -- Copyright (C) 2013-2018 Alexey Melnichuk 6 | -- 7 | -- Licensed according to the included 'LICENCE' document 8 | -- 9 | -- This file is part of lua-path library. 10 | -- 11 | ------------------------------------------------------------------ 12 | 13 | return{ 14 | _NAME = "path"; 15 | _VERSION = "0.3.1"; 16 | _COPYRIGHT = "Copyright (C) 2013-2018 Alexey Melnichuk"; 17 | _LICENSE = "MIT"; 18 | } -------------------------------------------------------------------------------- /test/run.lua: -------------------------------------------------------------------------------- 1 | 2 | function prequire(...) 3 | local ok, mod = pcall(require, ...) 4 | if ok then return mod, ... end 5 | return nil, mod 6 | end 7 | 8 | local lfs = prequire "lfs" 9 | 10 | prequire"luacov" 11 | 12 | print("------------------------------------") 13 | print("Lua version: " .. (_G.jit and _G.jit.version or _G._VERSION)) 14 | print("LFS version: " .. (lfs and (lfs._VERSION or "(unknown)") or "(not found)") ) 15 | print("Is Windows: " .. tostring(not not require"path".IS_WINDOWS)) 16 | print("PATH version: " .. require"path"._VERSION) 17 | print("------------------------------------") 18 | print("") 19 | 20 | local HAS_RUNNER = not not lunit 21 | local lunit = require "lunit" 22 | LUNIT_RUN = true 23 | 24 | require 'test_wcs' 25 | require 'test_fs' 26 | require 'test_each' 27 | require 'test' 28 | 29 | if not HAS_RUNNER then lunit.run() end -------------------------------------------------------------------------------- /lakefile: -------------------------------------------------------------------------------- 1 | PROJECT = 'path' 2 | 3 | J = J or path.join 4 | 5 | if LUA_VER == '5.3' then 6 | LUA_DIR = ENV.LUA_DIR_5_3 or ENV.LUA_DIR 7 | LUA_RUNNER = 'lua53' 8 | elseif LUA_VER == '5.2' then 9 | LUA_DIR = ENV.LUA_DIR_5_2 or ENV.LUA_DIR 10 | LUA_RUNNER = 'lua52' 11 | else 12 | LUA_DIR = ENV.LUA_DIR 13 | LUA_RUNNER = 'lua' 14 | end 15 | 16 | ROOT = ROOT or J(LUA_DIR,'libs',PROJECT) 17 | LUADIR = LUADIR or J(ROOT, 'lua') 18 | 19 | install = target('install', { 20 | file.group{odir= LUADIR; recurse = true; src = J('lua', '*.*' )}; 21 | file.group{odir=J(ROOT, 'test'); recurse = true; src = J('test', '*.*' )}; 22 | }) 23 | 24 | local function run(file, cwd) 25 | print() 26 | print("run " .. file) 27 | if not TESTING then 28 | if cwd then lake.chdir(cwd) end 29 | os.execute( LUA_RUNNER .. ' ' .. file ) 30 | if cwd then lake.chdir("<") end 31 | print() 32 | end 33 | end 34 | 35 | target('test', install, function() 36 | local test_dir = J(ROOT,'test') 37 | run(J(test_dir,'run.lua'), test_dir) 38 | run(J(test_dir,'test_lfs.lua'), test_dir) 39 | end) 40 | -------------------------------------------------------------------------------- /LICENCE.txt: -------------------------------------------------------------------------------- 1 | Copyright (C) 2013-2016 Alexey Melnichuk. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 14 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 15 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 16 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 17 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR 18 | ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 19 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE 21 | OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /test/.luacov: -------------------------------------------------------------------------------- 1 | return { 2 | configfile = ".luacov", 3 | 4 | -- filename to store stats collected 5 | statsfile = "luacov.stats.out", 6 | 7 | -- filename to store report 8 | reportfile = "luacov.report.json", 9 | 10 | -- Run reporter on completion? (won't work for ticks) 11 | runreport = false, 12 | 13 | -- Delete stats file after reporting? 14 | deletestats = false, 15 | 16 | -- Patterns for files to include when reporting 17 | -- all will be included if nothing is listed 18 | -- (exclude overrules include, do not include 19 | -- the .lua extension) 20 | include = { 21 | "/path$", 22 | "/path/.+$", 23 | }, 24 | 25 | -- Patterns for files to exclude when reporting 26 | -- all will be included if nothing is listed 27 | -- (exclude overrules include, do not include 28 | -- the .lua extension) 29 | exclude = {}, 30 | 31 | -- configuration for luacov-coveralls reporter 32 | coveralls = { 33 | 34 | -- debug = true, 35 | 36 | pathcorrect = { 37 | {"\\", "/" }; 38 | {"^.-[/\\]share[/\\]lua[/\\]5.%d", "lua" }; 39 | }, 40 | 41 | }, 42 | 43 | } 44 | -------------------------------------------------------------------------------- /lua/path/fs.lua: -------------------------------------------------------------------------------- 1 | ------------------------------------------------------------------ 2 | -- 3 | -- Author: Alexey Melnichuk 4 | -- 5 | -- Copyright (C) 2013-2018 Alexey Melnichuk 6 | -- 7 | -- Licensed according to the included 'LICENCE' document 8 | -- 9 | -- This file is part of lua-path library. 10 | -- 11 | ------------------------------------------------------------------ 12 | 13 | local DIR_SEP = package.config:sub(1,1) 14 | local IS_WINDOWS = DIR_SEP == '\\' 15 | 16 | local function prequire(m) 17 | local ok, err = pcall(require, m) 18 | if not ok then return nil, err end 19 | return err 20 | end 21 | 22 | local fs 23 | 24 | if not fs and IS_WINDOWS then 25 | local winfs = prequire"path.win32.fs" 26 | if winfs then 27 | local ok, mod = pcall(winfs.load, "ffi", "A") 28 | if not ok then ok, mod = pcall(winfs.load, "alien", "A") end 29 | fs = ok and mod 30 | end 31 | end 32 | 33 | if not fs and not IS_WINDOWS then 34 | fs = prequire"path.syscall.fs" 35 | end 36 | 37 | if not fs then 38 | fs = prequire"path.lfs.fs" 39 | end 40 | 41 | assert(fs, "you need installed LuaFileSystem or FFI/Alien (Windows only)") 42 | 43 | return fs 44 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required ( VERSION 2.8 ) 2 | 3 | project ( lua-path NONE ) 4 | include ( cmake/dist.cmake ) 5 | include ( lua ) 6 | 7 | install_lua_module ( path lua/path.lua ) 8 | install_lua_module ( path.fs lua/path/fs.lua ) 9 | install_lua_module ( path.findfile lua/path/findfile.lua ) 10 | install_lua_module ( path.lfs.fs lua/path/lfs/fs.lua ) 11 | install_lua_module ( path.syscall.fs lua/path/syscall/fs.lua ) 12 | install_lua_module ( path.lfs.impl.fs lua/path/lfs/impl/fs.lua ) 13 | install_lua_module ( path.module lua/path/module.lua ) 14 | install_lua_module ( path.win32.alien.fs lua/path/win32/alien/fs.lua ) 15 | install_lua_module ( path.win32.alien.types lua/path/win32/alien/types.lua ) 16 | install_lua_module ( path.win32.alien.utils lua/path/win32/alien/utils.lua ) 17 | install_lua_module ( path.win32.alien.wcs lua/path/win32/alien/wcs.lua ) 18 | install_lua_module ( path.win32.ffi.fs lua/path/win32/ffi/fs.lua ) 19 | install_lua_module ( path.win32.ffi.types lua/path/win32/ffi/types.lua ) 20 | install_lua_module ( path.win32.ffi.wcs lua/path/win32/ffi/wcs.lua ) 21 | install_lua_module ( path.win32.fs lua/path/win32/fs.lua ) 22 | install_lua_module ( path.win32.wcs lua/path/win32/wcs.lua ) 23 | 24 | install_data ( README.md LICENCE.txt ) 25 | 26 | install_test ( test/ ) 27 | -------------------------------------------------------------------------------- /rockspecs/lua-path-0.1.0-1.rockspec: -------------------------------------------------------------------------------- 1 | package = "lua-path" 2 | version = "0.1.0-1" 3 | source = { 4 | url = "https://github.com/moteus/lua-path/archive/v0.1.0.zip", 5 | dir = "lua-path-0.1.0", 6 | } 7 | 8 | description = { 9 | summary = "File system path manipulation library", 10 | detailed = [[ 11 | ]], 12 | homepage = "https://github.com/moteus/lua-path", 13 | license = "MIT/X11", 14 | } 15 | 16 | dependencies = { 17 | "lua >= 5.1", 18 | -- "luafilesystem >= 1.4", 19 | -- "alien >= 0.7.0", -- instead lfs on windows 20 | } 21 | 22 | build = { 23 | type = "builtin", 24 | copy_directories = { 25 | "test", 26 | }, 27 | modules = { 28 | ["path" ] = "lua/path.lua", 29 | ["path.findfile" ] = "lua/path/findfile.lua", 30 | ["path.lfs.fs" ] = "lua/path/lfs/fs.lua", 31 | ["path.module" ] = "lua/path/module.lua", 32 | ["path.win32.alien.fs" ] = "lua/path/win32/alien/fs.lua", 33 | ["path.win32.alien.types" ] = "lua/path/win32/alien/types.lua", 34 | ["path.win32.alien.utils" ] = "lua/path/win32/alien/utils.lua", 35 | ["path.win32.alien.wcs" ] = "lua/path/win32/alien/wcs.lua", 36 | ["path.win32.ffi.fs" ] = "lua/path/win32/ffi/fs.lua", 37 | ["path.win32.ffi.types" ] = "lua/path/win32/ffi/types.lua", 38 | ["path.win32.ffi.wcs" ] = "lua/path/win32/ffi/wcs.lua", 39 | ["path.win32.fs" ] = "lua/path/win32/fs.lua", 40 | ["path.win32.wcs" ] = "lua/path/win32/wcs.lua", 41 | } 42 | } 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /rockspecs/lua-path-0.1.1-1.rockspec: -------------------------------------------------------------------------------- 1 | package = "lua-path" 2 | version = "0.1.1-1" 3 | source = { 4 | url = "https://github.com/moteus/lua-path/archive/v0.1.1.zip", 5 | dir = "lua-path-0.1.1", 6 | } 7 | 8 | description = { 9 | summary = "File system path manipulation library", 10 | detailed = [[ 11 | ]], 12 | homepage = "https://github.com/moteus/lua-path", 13 | license = "MIT/X11", 14 | } 15 | 16 | dependencies = { 17 | "lua >= 5.1, < 5.3", 18 | -- "luafilesystem >= 1.4", 19 | -- "alien >= 0.7.0", -- instead lfs on windows 20 | } 21 | 22 | build = { 23 | type = "builtin", 24 | copy_directories = { 25 | "test", 26 | }, 27 | modules = { 28 | ["path" ] = "lua/path.lua", 29 | ["path.findfile" ] = "lua/path/findfile.lua", 30 | ["path.lfs.fs" ] = "lua/path/lfs/fs.lua", 31 | ["path.module" ] = "lua/path/module.lua", 32 | ["path.win32.alien.fs" ] = "lua/path/win32/alien/fs.lua", 33 | ["path.win32.alien.types" ] = "lua/path/win32/alien/types.lua", 34 | ["path.win32.alien.utils" ] = "lua/path/win32/alien/utils.lua", 35 | ["path.win32.alien.wcs" ] = "lua/path/win32/alien/wcs.lua", 36 | ["path.win32.ffi.fs" ] = "lua/path/win32/ffi/fs.lua", 37 | ["path.win32.ffi.types" ] = "lua/path/win32/ffi/types.lua", 38 | ["path.win32.ffi.wcs" ] = "lua/path/win32/ffi/wcs.lua", 39 | ["path.win32.fs" ] = "lua/path/win32/fs.lua", 40 | ["path.win32.wcs" ] = "lua/path/win32/wcs.lua", 41 | } 42 | } 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # lua-path 2 | 3 | [![Licence](http://img.shields.io/badge/Licence-MIT-brightgreen.svg)](LICENCE.txt) 4 | [![Build Status](https://travis-ci.org/moteus/lua-path.png?branch=master)](https://travis-ci.org/moteus/lua-path) 5 | [![Build Status](https://ci.appveyor.com/api/projects/status/okrhcb519rldjhn4?svg=true)](https://ci.appveyor.com/project/moteus/lua-path) 6 | [![Coverage Status](https://coveralls.io/repos/moteus/lua-path/badge.png)](https://coveralls.io/r/moteus/lua-path) 7 | 8 | ## Documentation 9 | 10 | [View entire documentation here](http://moteus.github.io/path/index.html) 11 | 12 | ## Usage 13 | ```lua 14 | local PATH = require "path" 15 | 16 | -- suppose we run on windows 17 | assert(PATH.IS_WINDOWS) 18 | 19 | -- we can use system dependet function 20 | print(PATH.user_home()) -- C:\Documents and Settings\Admin 21 | print(PATH.currentdir()) -- C:\lua\5.1 22 | 23 | -- but we can use specific system path notation 24 | local ftp_path = PATH.new('/') 25 | print(ftp_path.join("/root", "some", "dir")) -- /root/some/dir 26 | 27 | -- All functions specific to system will fail 28 | assert(not pcall( ftp_path.currentdir ) ) 29 | ``` 30 | 31 | ```lua 32 | -- clean currentdir 33 | 34 | local path = require "path" 35 | path.each("./*", function(P, mode) 36 | if mode == 'directory' then 37 | path.rmdir(P) 38 | else 39 | path.remove(P) 40 | end 41 | end,{ 42 | param = "fm"; -- request full path and mode 43 | delay = true; -- use snapshot of directory 44 | recurse = true; -- include subdirs 45 | reverse = true; -- subdirs at first 46 | }) 47 | ``` 48 | 49 | -------------------------------------------------------------------------------- /rockspecs/lua-path-0.2.0-1.rockspec: -------------------------------------------------------------------------------- 1 | package = "lua-path" 2 | version = "0.2.0-1" 3 | source = { 4 | url = "https://github.com/moteus/lua-path/archive/v0.2.0.zip", 5 | dir = "lua-path-0.2.0", 6 | } 7 | 8 | description = { 9 | summary = "File system path manipulation library", 10 | detailed = [[ 11 | ]], 12 | homepage = "https://github.com/moteus/lua-path", 13 | license = "MIT/X11", 14 | } 15 | 16 | dependencies = { 17 | "lua >= 5.1, < 5.3", 18 | -- "luafilesystem >= 1.4", 19 | -- "alien >= 0.7.0", -- instead lfs on windows 20 | } 21 | 22 | build = { 23 | type = "builtin", 24 | copy_directories = { 25 | "test", 26 | }, 27 | modules = { 28 | ["path" ] = "lua/path.lua", 29 | ["path.fs" ] = "lua/path/fs.lua", 30 | ["path.findfile" ] = "lua/path/findfile.lua", 31 | ["path.lfs.fs" ] = "lua/path/lfs/fs.lua", 32 | ["path.module" ] = "lua/path/module.lua", 33 | ["path.win32.alien.fs" ] = "lua/path/win32/alien/fs.lua", 34 | ["path.win32.alien.types" ] = "lua/path/win32/alien/types.lua", 35 | ["path.win32.alien.utils" ] = "lua/path/win32/alien/utils.lua", 36 | ["path.win32.alien.wcs" ] = "lua/path/win32/alien/wcs.lua", 37 | ["path.win32.ffi.fs" ] = "lua/path/win32/ffi/fs.lua", 38 | ["path.win32.ffi.types" ] = "lua/path/win32/ffi/types.lua", 39 | ["path.win32.ffi.wcs" ] = "lua/path/win32/ffi/wcs.lua", 40 | ["path.win32.fs" ] = "lua/path/win32/fs.lua", 41 | ["path.win32.wcs" ] = "lua/path/win32/wcs.lua", 42 | } 43 | } 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | version: 0.3.2.{build} 2 | 3 | os: 4 | - Windows Server 2012 R2 5 | 6 | shallow_clone: true 7 | 8 | environment: 9 | LR_EXTERNAL: c:\external 10 | CURL_VER: 7.53.1 11 | 12 | matrix: 13 | - LUA: "lua 5.1" 14 | - LUA: "lua 5.2" 15 | - LUA: "lua 5.3" 16 | - LUA: "lua 5.4" 17 | - LUA: "luajit 2.1" 18 | 19 | platform: 20 | - x64 21 | # - x86 22 | # - mingw 23 | 24 | cache: 25 | - c:\hererocks -> appveyor.yml 26 | - c:\external -> appveyor.yml 27 | 28 | install: 29 | - set PATH=C:\Python27\Scripts;%LR_EXTERNAL%;%PATH% 30 | - if /I "%platform%"=="x86" set HR_TARGET=vs_32 31 | - if /I "%platform%"=="x64" set HR_TARGET=vs_64 32 | - if /I "%platform%"=="mingw" set HR_TARGET=mingw 33 | - if /I "%platform%"=="mingw" set PATH=C:\MinGW\bin;%PATH% 34 | - if not exist "%LR_EXTERNAL%" ( 35 | mkdir "%LR_EXTERNAL%" && 36 | mkdir "%LR_EXTERNAL%\lib" && 37 | mkdir "%LR_EXTERNAL%\include" 38 | ) 39 | - if not exist c:\hererocks ( 40 | pip install hererocks && 41 | hererocks c:\hererocks --%LUA% --target %HR_TARGET% -rlatest 42 | ) 43 | - call c:\hererocks\bin\activate 44 | 45 | before_build: 46 | - luarocks show luafilesystem >nul 2>&1 || luarocks install luafilesystem 47 | 48 | build_script: 49 | - luarocks make rockspecs/lua-path-scm-0.rockspec 50 | 51 | before_test: 52 | - luarocks show lunitx >nul 2>&1 || luarocks install lunitx 53 | - luarocks show luacov >nul 2>&1 || luarocks install luacov 54 | 55 | test_script: 56 | - cd %APPVEYOR_BUILD_FOLDER%\test 57 | - lua run.lua 58 | - lua -lluacov test_lfs.lua 59 | 60 | after_test: 61 | # @todo 62 | -------------------------------------------------------------------------------- /rockspecs/lua-path-0.2.3-1.rockspec: -------------------------------------------------------------------------------- 1 | package = "lua-path" 2 | version = "0.2.3-1" 3 | source = { 4 | url = "https://github.com/moteus/lua-path/archive/v0.2.3.zip", 5 | dir = "lua-path-0.2.3", 6 | } 7 | 8 | description = { 9 | summary = "File system path manipulation library", 10 | detailed = [[ 11 | ]], 12 | homepage = "https://github.com/moteus/lua-path", 13 | license = "MIT/X11", 14 | } 15 | 16 | dependencies = { 17 | "lua >= 5.1, < 5.4", 18 | -- "luafilesystem >= 1.4", 19 | -- "alien >= 0.7.0", -- instead lfs on windows 20 | } 21 | 22 | build = { 23 | type = "builtin", 24 | copy_directories = { 25 | "test", 26 | }, 27 | modules = { 28 | ["path" ] = "lua/path.lua", 29 | ["path.fs" ] = "lua/path/fs.lua", 30 | ["path.findfile" ] = "lua/path/findfile.lua", 31 | ["path.lfs.fs" ] = "lua/path/lfs/fs.lua", 32 | ["path.syscall.fs" ] = "lua/path/syscall/fs.lua", 33 | ["path.lfs.impl.fs" ] = "lua/path/lfs/impl/fs.lua", 34 | ["path.module" ] = "lua/path/module.lua", 35 | ["path.win32.alien.fs" ] = "lua/path/win32/alien/fs.lua", 36 | ["path.win32.alien.types" ] = "lua/path/win32/alien/types.lua", 37 | ["path.win32.alien.utils" ] = "lua/path/win32/alien/utils.lua", 38 | ["path.win32.alien.wcs" ] = "lua/path/win32/alien/wcs.lua", 39 | ["path.win32.ffi.fs" ] = "lua/path/win32/ffi/fs.lua", 40 | ["path.win32.ffi.types" ] = "lua/path/win32/ffi/types.lua", 41 | ["path.win32.ffi.wcs" ] = "lua/path/win32/ffi/wcs.lua", 42 | ["path.win32.fs" ] = "lua/path/win32/fs.lua", 43 | ["path.win32.wcs" ] = "lua/path/win32/wcs.lua", 44 | } 45 | } 46 | 47 | -------------------------------------------------------------------------------- /rockspecs/lua-path-0.2.4-1.rockspec: -------------------------------------------------------------------------------- 1 | package = "lua-path" 2 | version = "0.2.4-1" 3 | source = { 4 | url = "https://github.com/moteus/lua-path/archive/v0.2.4.zip", 5 | dir = "lua-path-0.2.4", 6 | } 7 | 8 | description = { 9 | summary = "File system path manipulation library", 10 | detailed = [[ 11 | ]], 12 | homepage = "https://github.com/moteus/lua-path", 13 | license = "MIT/X11", 14 | } 15 | 16 | dependencies = { 17 | "lua >= 5.1, < 5.4", 18 | -- "luafilesystem >= 1.4", 19 | -- "alien >= 0.7.0", -- instead lfs on windows 20 | } 21 | 22 | build = { 23 | type = "builtin", 24 | copy_directories = { 25 | "test", 26 | }, 27 | modules = { 28 | ["path" ] = "lua/path.lua", 29 | ["path.fs" ] = "lua/path/fs.lua", 30 | ["path.findfile" ] = "lua/path/findfile.lua", 31 | ["path.lfs.fs" ] = "lua/path/lfs/fs.lua", 32 | ["path.syscall.fs" ] = "lua/path/syscall/fs.lua", 33 | ["path.lfs.impl.fs" ] = "lua/path/lfs/impl/fs.lua", 34 | ["path.module" ] = "lua/path/module.lua", 35 | ["path.win32.alien.fs" ] = "lua/path/win32/alien/fs.lua", 36 | ["path.win32.alien.types" ] = "lua/path/win32/alien/types.lua", 37 | ["path.win32.alien.utils" ] = "lua/path/win32/alien/utils.lua", 38 | ["path.win32.alien.wcs" ] = "lua/path/win32/alien/wcs.lua", 39 | ["path.win32.ffi.fs" ] = "lua/path/win32/ffi/fs.lua", 40 | ["path.win32.ffi.types" ] = "lua/path/win32/ffi/types.lua", 41 | ["path.win32.ffi.wcs" ] = "lua/path/win32/ffi/wcs.lua", 42 | ["path.win32.fs" ] = "lua/path/win32/fs.lua", 43 | ["path.win32.wcs" ] = "lua/path/win32/wcs.lua", 44 | } 45 | } 46 | 47 | -------------------------------------------------------------------------------- /rockspecs/lua-path-0.3.0-1.rockspec: -------------------------------------------------------------------------------- 1 | package = "lua-path" 2 | version = "0.3.0-1" 3 | source = { 4 | url = "https://github.com/moteus/lua-path/archive/v0.3.0.zip", 5 | dir = "lua-path-0.3.0", 6 | } 7 | 8 | description = { 9 | summary = "File system path manipulation library", 10 | detailed = [[ 11 | ]], 12 | homepage = "https://github.com/moteus/lua-path", 13 | license = "MIT/X11", 14 | } 15 | 16 | dependencies = { 17 | "lua >= 5.1, < 5.4", 18 | -- "luafilesystem >= 1.4", 19 | -- "alien >= 0.7.0", -- instead lfs on windows 20 | } 21 | 22 | build = { 23 | type = "builtin", 24 | copy_directories = { 25 | "test", 26 | }, 27 | modules = { 28 | ["path" ] = "lua/path.lua", 29 | ["path.fs" ] = "lua/path/fs.lua", 30 | ["path.findfile" ] = "lua/path/findfile.lua", 31 | ["path.lfs.fs" ] = "lua/path/lfs/fs.lua", 32 | ["path.syscall.fs" ] = "lua/path/syscall/fs.lua", 33 | ["path.lfs.impl.fs" ] = "lua/path/lfs/impl/fs.lua", 34 | ["path.module" ] = "lua/path/module.lua", 35 | ["path.win32.alien.fs" ] = "lua/path/win32/alien/fs.lua", 36 | ["path.win32.alien.types" ] = "lua/path/win32/alien/types.lua", 37 | ["path.win32.alien.utils" ] = "lua/path/win32/alien/utils.lua", 38 | ["path.win32.alien.wcs" ] = "lua/path/win32/alien/wcs.lua", 39 | ["path.win32.ffi.fs" ] = "lua/path/win32/ffi/fs.lua", 40 | ["path.win32.ffi.types" ] = "lua/path/win32/ffi/types.lua", 41 | ["path.win32.ffi.wcs" ] = "lua/path/win32/ffi/wcs.lua", 42 | ["path.win32.fs" ] = "lua/path/win32/fs.lua", 43 | ["path.win32.wcs" ] = "lua/path/win32/wcs.lua", 44 | } 45 | } 46 | 47 | -------------------------------------------------------------------------------- /rockspecs/lua-path-0.2.1-1.rockspec: -------------------------------------------------------------------------------- 1 | package = "lua-path" 2 | version = "0.2.1-1" 3 | source = { 4 | url = "https://github.com/moteus/lua-path/archive/v0.2.1.zip", 5 | dir = "lua-path-0.2.1", 6 | } 7 | 8 | description = { 9 | summary = "File system path manipulation library", 10 | detailed = [[ 11 | ]], 12 | homepage = "https://github.com/moteus/lua-path", 13 | license = "MIT/X11", 14 | } 15 | 16 | dependencies = { 17 | "lua >= 5.1, < 5.3", 18 | -- "luafilesystem >= 1.4", 19 | -- "alien >= 0.7.0", -- instead lfs on windows 20 | } 21 | 22 | build = { 23 | type = "builtin", 24 | copy_directories = { 25 | "test", 26 | }, 27 | modules = { 28 | ["path" ] = "lua/path.lua", 29 | ["path.fs" ] = "lua/path/fs.lua", 30 | ["path.findfile" ] = "lua/path/findfile.lua", 31 | ["path.lfs.fs" ] = "lua/path/lfs/fs.lua", 32 | ["path.syscall.fs" ] = "lua/path/syscall/fs.lua", 33 | ["path.lfs.impl.fs" ] = "lua/path/lfs/impl/fs.lua", 34 | ["path.module" ] = "lua/path/module.lua", 35 | ["path.win32.alien.fs" ] = "lua/path/win32/alien/fs.lua", 36 | ["path.win32.alien.types" ] = "lua/path/win32/alien/types.lua", 37 | ["path.win32.alien.utils" ] = "lua/path/win32/alien/utils.lua", 38 | ["path.win32.alien.wcs" ] = "lua/path/win32/alien/wcs.lua", 39 | ["path.win32.ffi.fs" ] = "lua/path/win32/ffi/fs.lua", 40 | ["path.win32.ffi.types" ] = "lua/path/win32/ffi/types.lua", 41 | ["path.win32.ffi.wcs" ] = "lua/path/win32/ffi/wcs.lua", 42 | ["path.win32.fs" ] = "lua/path/win32/fs.lua", 43 | ["path.win32.wcs" ] = "lua/path/win32/wcs.lua", 44 | } 45 | } 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /rockspecs/lua-path-0.2.2-1.rockspec: -------------------------------------------------------------------------------- 1 | package = "lua-path" 2 | version = "0.2.2-1" 3 | source = { 4 | url = "https://github.com/moteus/lua-path/archive/v0.2.2.zip", 5 | dir = "lua-path-0.2.2", 6 | } 7 | 8 | description = { 9 | summary = "File system path manipulation library", 10 | detailed = [[ 11 | ]], 12 | homepage = "https://github.com/moteus/lua-path", 13 | license = "MIT/X11", 14 | } 15 | 16 | dependencies = { 17 | "lua >= 5.1, < 5.3", 18 | -- "luafilesystem >= 1.4", 19 | -- "alien >= 0.7.0", -- instead lfs on windows 20 | } 21 | 22 | build = { 23 | type = "builtin", 24 | copy_directories = { 25 | "test", 26 | }, 27 | modules = { 28 | ["path" ] = "lua/path.lua", 29 | ["path.fs" ] = "lua/path/fs.lua", 30 | ["path.findfile" ] = "lua/path/findfile.lua", 31 | ["path.lfs.fs" ] = "lua/path/lfs/fs.lua", 32 | ["path.syscall.fs" ] = "lua/path/syscall/fs.lua", 33 | ["path.lfs.impl.fs" ] = "lua/path/lfs/impl/fs.lua", 34 | ["path.module" ] = "lua/path/module.lua", 35 | ["path.win32.alien.fs" ] = "lua/path/win32/alien/fs.lua", 36 | ["path.win32.alien.types" ] = "lua/path/win32/alien/types.lua", 37 | ["path.win32.alien.utils" ] = "lua/path/win32/alien/utils.lua", 38 | ["path.win32.alien.wcs" ] = "lua/path/win32/alien/wcs.lua", 39 | ["path.win32.ffi.fs" ] = "lua/path/win32/ffi/fs.lua", 40 | ["path.win32.ffi.types" ] = "lua/path/win32/ffi/types.lua", 41 | ["path.win32.ffi.wcs" ] = "lua/path/win32/ffi/wcs.lua", 42 | ["path.win32.fs" ] = "lua/path/win32/fs.lua", 43 | ["path.win32.wcs" ] = "lua/path/win32/wcs.lua", 44 | } 45 | } 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /rockspecs/lua-path-0.3.0-2.rockspec: -------------------------------------------------------------------------------- 1 | package = "lua-path" 2 | version = "0.3.0-2" 3 | source = { 4 | url = "https://github.com/moteus/lua-path/archive/v0.3.0.zip", 5 | dir = "lua-path-0.3.0", 6 | } 7 | 8 | description = { 9 | summary = "File system path manipulation library", 10 | detailed = [[ 11 | ]], 12 | homepage = "https://github.com/moteus/lua-path", 13 | license = "MIT/X11", 14 | } 15 | 16 | dependencies = { 17 | "lua >= 5.1, < 5.4", 18 | -- "luafilesystem >= 1.4", 19 | -- "alien >= 0.7.0", -- instead lfs on windows 20 | } 21 | 22 | build = { 23 | type = "builtin", 24 | copy_directories = { 25 | "doc", 26 | "examples", 27 | "test", 28 | }, 29 | modules = { 30 | ["path" ] = "lua/path.lua", 31 | ["path.fs" ] = "lua/path/fs.lua", 32 | ["path.findfile" ] = "lua/path/findfile.lua", 33 | ["path.lfs.fs" ] = "lua/path/lfs/fs.lua", 34 | ["path.syscall.fs" ] = "lua/path/syscall/fs.lua", 35 | ["path.lfs.impl.fs" ] = "lua/path/lfs/impl/fs.lua", 36 | ["path.module" ] = "lua/path/module.lua", 37 | ["path.win32.alien.fs" ] = "lua/path/win32/alien/fs.lua", 38 | ["path.win32.alien.types" ] = "lua/path/win32/alien/types.lua", 39 | ["path.win32.alien.utils" ] = "lua/path/win32/alien/utils.lua", 40 | ["path.win32.alien.wcs" ] = "lua/path/win32/alien/wcs.lua", 41 | ["path.win32.ffi.fs" ] = "lua/path/win32/ffi/fs.lua", 42 | ["path.win32.ffi.types" ] = "lua/path/win32/ffi/types.lua", 43 | ["path.win32.ffi.wcs" ] = "lua/path/win32/ffi/wcs.lua", 44 | ["path.win32.fs" ] = "lua/path/win32/fs.lua", 45 | ["path.win32.wcs" ] = "lua/path/win32/wcs.lua", 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /rockspecs/lua-path-0.3.1-1.rockspec: -------------------------------------------------------------------------------- 1 | package = "lua-path" 2 | version = "0.3.1-1" 3 | source = { 4 | url = "https://github.com/moteus/lua-path/archive/v0.3.1.zip", 5 | dir = "lua-path-0.3.1", 6 | } 7 | 8 | description = { 9 | summary = "File system path manipulation library", 10 | detailed = [[ 11 | ]], 12 | homepage = "https://github.com/moteus/lua-path", 13 | license = "MIT/X11", 14 | } 15 | 16 | dependencies = { 17 | "lua >= 5.1, < 5.4", 18 | -- "luafilesystem >= 1.4", 19 | -- "alien >= 0.7.0", -- instead lfs on windows 20 | } 21 | 22 | build = { 23 | type = "builtin", 24 | copy_directories = { 25 | "doc", 26 | "examples", 27 | "test", 28 | }, 29 | modules = { 30 | ["path" ] = "lua/path.lua", 31 | ["path.fs" ] = "lua/path/fs.lua", 32 | ["path.findfile" ] = "lua/path/findfile.lua", 33 | ["path.lfs.fs" ] = "lua/path/lfs/fs.lua", 34 | ["path.syscall.fs" ] = "lua/path/syscall/fs.lua", 35 | ["path.lfs.impl.fs" ] = "lua/path/lfs/impl/fs.lua", 36 | ["path.module" ] = "lua/path/module.lua", 37 | ["path.win32.alien.fs" ] = "lua/path/win32/alien/fs.lua", 38 | ["path.win32.alien.types" ] = "lua/path/win32/alien/types.lua", 39 | ["path.win32.alien.utils" ] = "lua/path/win32/alien/utils.lua", 40 | ["path.win32.alien.wcs" ] = "lua/path/win32/alien/wcs.lua", 41 | ["path.win32.ffi.fs" ] = "lua/path/win32/ffi/fs.lua", 42 | ["path.win32.ffi.types" ] = "lua/path/win32/ffi/types.lua", 43 | ["path.win32.ffi.wcs" ] = "lua/path/win32/ffi/wcs.lua", 44 | ["path.win32.fs" ] = "lua/path/win32/fs.lua", 45 | ["path.win32.wcs" ] = "lua/path/win32/wcs.lua", 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /rockspecs/lua-path-0.3.1-2.rockspec: -------------------------------------------------------------------------------- 1 | package = "lua-path" 2 | version = "0.3.1-2" 3 | source = { 4 | url = "https://github.com/moteus/lua-path/archive/v0.3.1.zip", 5 | dir = "lua-path-0.3.1", 6 | } 7 | 8 | description = { 9 | summary = "File system path manipulation library", 10 | detailed = [[ 11 | ]], 12 | homepage = "https://github.com/moteus/lua-path", 13 | license = "MIT/X11", 14 | } 15 | 16 | dependencies = { 17 | "lua >= 5.1, < 5.5", 18 | -- "luafilesystem >= 1.4", 19 | -- "alien >= 0.7.0", -- instead lfs on windows 20 | } 21 | 22 | build = { 23 | type = "builtin", 24 | copy_directories = { 25 | "doc", 26 | "examples", 27 | "test", 28 | }, 29 | modules = { 30 | ["path" ] = "lua/path.lua", 31 | ["path.fs" ] = "lua/path/fs.lua", 32 | ["path.findfile" ] = "lua/path/findfile.lua", 33 | ["path.lfs.fs" ] = "lua/path/lfs/fs.lua", 34 | ["path.syscall.fs" ] = "lua/path/syscall/fs.lua", 35 | ["path.lfs.impl.fs" ] = "lua/path/lfs/impl/fs.lua", 36 | ["path.module" ] = "lua/path/module.lua", 37 | ["path.win32.alien.fs" ] = "lua/path/win32/alien/fs.lua", 38 | ["path.win32.alien.types" ] = "lua/path/win32/alien/types.lua", 39 | ["path.win32.alien.utils" ] = "lua/path/win32/alien/utils.lua", 40 | ["path.win32.alien.wcs" ] = "lua/path/win32/alien/wcs.lua", 41 | ["path.win32.ffi.fs" ] = "lua/path/win32/ffi/fs.lua", 42 | ["path.win32.ffi.types" ] = "lua/path/win32/ffi/types.lua", 43 | ["path.win32.ffi.wcs" ] = "lua/path/win32/ffi/wcs.lua", 44 | ["path.win32.fs" ] = "lua/path/win32/fs.lua", 45 | ["path.win32.wcs" ] = "lua/path/win32/wcs.lua", 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /rockspecs/lua-path-scm-0.rockspec: -------------------------------------------------------------------------------- 1 | package = "lua-path" 2 | version = "scm-0" 3 | source = { 4 | url = "https://github.com/moteus/lua-path/archive/master.zip", 5 | dir = "lua-path-master", 6 | } 7 | 8 | description = { 9 | summary = "File system path manipulation library", 10 | detailed = [[ 11 | ]], 12 | homepage = "https://github.com/moteus/lua-path", 13 | license = "MIT/X11", 14 | } 15 | 16 | dependencies = { 17 | "lua >= 5.1, < 5.5", 18 | -- "luafilesystem >= 1.4", 19 | -- "alien >= 0.7.0", -- instead lfs on windows 20 | } 21 | 22 | build = { 23 | type = "builtin", 24 | copy_directories = { 25 | "doc", 26 | "examples", 27 | "test", 28 | }, 29 | modules = { 30 | ["path" ] = "lua/path.lua", 31 | ["path.fs" ] = "lua/path/fs.lua", 32 | ["path.findfile" ] = "lua/path/findfile.lua", 33 | ["path.lfs.fs" ] = "lua/path/lfs/fs.lua", 34 | ["path.syscall.fs" ] = "lua/path/syscall/fs.lua", 35 | ["path.lfs.impl.fs" ] = "lua/path/lfs/impl/fs.lua", 36 | ["path.module" ] = "lua/path/module.lua", 37 | ["path.win32.alien.fs" ] = "lua/path/win32/alien/fs.lua", 38 | ["path.win32.alien.types" ] = "lua/path/win32/alien/types.lua", 39 | ["path.win32.alien.utils" ] = "lua/path/win32/alien/utils.lua", 40 | ["path.win32.alien.wcs" ] = "lua/path/win32/alien/wcs.lua", 41 | ["path.win32.ffi.fs" ] = "lua/path/win32/ffi/fs.lua", 42 | ["path.win32.ffi.types" ] = "lua/path/win32/ffi/types.lua", 43 | ["path.win32.ffi.wcs" ] = "lua/path/win32/ffi/wcs.lua", 44 | ["path.win32.fs" ] = "lua/path/win32/fs.lua", 45 | ["path.win32.wcs" ] = "lua/path/win32/wcs.lua", 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: c 2 | 3 | sudo: false 4 | 5 | matrix: 6 | include: 7 | - env: LUA="lua 5.1" 8 | os: osx 9 | - env: LUA="lua 5.1" 10 | os: linux 11 | - env: LUA="lua 5.2" 12 | os: linux 13 | - env: LUA="lua 5.3" 14 | os: linux 15 | - env: LUA="lua 5.4" 16 | os: linux 17 | - env: LUA="luajit 2.0" 18 | os: linux 19 | - env: LUA="luajit 2.1" 20 | os: linux 21 | - env: LUA="luajit 2.1" 22 | os: osx 23 | 24 | cache: 25 | directories: 26 | - here 27 | - $HOME/.cache/pip 28 | 29 | branches: 30 | only: 31 | - master 32 | 33 | before_install: 34 | - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then export PATH=$PATH:~/Library/Python/2.7/bin/; fi 35 | - pip install --user cpp-coveralls 36 | - pip install --user hererocks 37 | - hererocks here -r^ --$LUA 38 | - source here/bin/activate 39 | - if [ "${LUA:0:6}" == "luajit" ]; then 40 | luarocks show ljsyscall > /dev/null 2>&1 || luarocks install ljsyscall; 41 | fi 42 | - luarocks show luafilesystem > /dev/null 2>&1 || luarocks install luafilesystem 43 | 44 | install: 45 | - luarocks make rockspecs/lua-path-scm-0.rockspec 46 | 47 | before_script: 48 | - luarocks show luacov > /dev/null 2>&1 || luarocks install luacov 49 | - luarocks show lunitx > /dev/null 2>&1 || luarocks install lunitx 50 | 51 | script: 52 | - cd test 53 | - lunit.sh run.lua 54 | - lua -lluacov test_lfs.lua 55 | 56 | after_success: 57 | - luarocks show lua-curl > /dev/null 2>&1 || luarocks install lua-curl 58 | - luarocks show luacov-coveralls > /dev/null 2>&1 || luarocks install luacov-coveralls 59 | - luacov-coveralls 60 | 61 | notifications: 62 | email: 63 | on_success: change 64 | on_failure: always 65 | -------------------------------------------------------------------------------- /lua/path/win32/wcs.lua: -------------------------------------------------------------------------------- 1 | ------------------------------------------------------------------ 2 | -- 3 | -- Author: Alexey Melnichuk 4 | -- 5 | -- Copyright (C) 2013-2016 Alexey Melnichuk 6 | -- 7 | -- Licensed according to the included 'LICENCE' document 8 | -- 9 | -- This file is part of lua-path library. 10 | -- 11 | ------------------------------------------------------------------ 12 | 13 | local CP_ACP = 0 -- default to ANSI code page 14 | local CP_OEM = 1 -- default to OEM code page 15 | local CP_MAC = 2 -- default to MAC code page 16 | local CP_THREAD_ACP = 3 -- current thread's ANSI code page 17 | local CP_SYMBOL = 42 -- SYMBOL translations 18 | local CP_UTF7 = 65000 -- UTF-7 translation 19 | local CP_UTF8 = 65001 -- UTF-8 translation 20 | 21 | local LOADED = {} 22 | 23 | local function load(type) 24 | if LOADED[type] then return LOADED[type] end 25 | local IMPL = require("path.win32." .. type ..".wcs") 26 | 27 | 28 | local wcstoutf8 = function (str) return IMPL.wcstombs(str, CP_UTF8) end 29 | local utf8towcs = function (str) return IMPL.mbstowcs(str, CP_UTF8) end 30 | 31 | local wcstoansi = function (str) return IMPL.wcstombs(str, CP_ACP) end 32 | local ansitowcs = function (str) return IMPL.mbstowcs(str, CP_ACP) end 33 | 34 | local wcstooem = function (str) return IMPL.wcstombs(str, CP_OEM) end 35 | local oemtowcs = function (str) return IMPL.mbstowcs(str, CP_OEM) end 36 | 37 | local _M = { 38 | MultiByteToWideChar = IMPL.MultiByteToWideChar; 39 | WideCharToMultiByte = IMPL.WideCharToMultiByte; 40 | mbstowcs = IMPL.mbstowcs; 41 | wcstombs = IMPL.wcstombs; 42 | wcstoutf8 = wcstoutf8; 43 | utf8towcs = utf8towcs; 44 | wcstoansi = wcstoansi; 45 | ansitowcs = ansitowcs; 46 | wcstooem = wcstooem; 47 | oemtowcs = oemtowcs; 48 | CP = { 49 | ACP = CP_ACP; 50 | OEM = CP_OEM; 51 | MAC = CP_MAC; 52 | THREAD_ACP = CP_THREAD_ACP; 53 | SYMBOL = CP_SYMBOL; 54 | UTF7 = CP_UTF7; 55 | UTF8 = CP_UTF8; 56 | } 57 | } 58 | 59 | LOADED[type] = _M 60 | return _M 61 | end 62 | 63 | return { 64 | load = load 65 | } -------------------------------------------------------------------------------- /examples/walk_old_files.lua: -------------------------------------------------------------------------------- 1 | local path = require "path" 2 | local date = require "date" 3 | 4 | local function older_then(days) 5 | local now = date() 6 | return function (fname, ftime) 7 | return days <= date.diff(now, date(ftime):tolocal()):spandays() 8 | end 9 | end 10 | 11 | local function old_files_(mask, days, cmd) 12 | assert(days) 13 | assert(cmd) 14 | 15 | -- признак того что в директории есть файлы не поподающие под фильтр 16 | local has_more_files = false 17 | local filter = older_then(days) 18 | local last_fname, last_fdate 19 | path.each{file = mask, param = 'ft', 20 | recurse=false, skipdirs=true, 21 | callback = 22 | function(fname, ftime) 23 | if filter(fname, ftime) then 24 | if not last_fdate then -- первый попавшийся файл 25 | last_fname, last_fdate = fname, date(ftime) 26 | return 27 | end 28 | 29 | -- перед этим уже бал найден файл 30 | local next_fname, next_fdate = fname, date(ftime) 31 | 32 | -- находим самый "старый" файл 33 | if last_fdate < next_fdate then 34 | last_fdate, next_fdate = next_fdate, last_fdate 35 | last_fname, next_fname = next_fname, last_fname 36 | end 37 | 38 | cmd(next_fname, false) 39 | else 40 | -- в директории есть файлы которые поподают под маску и не поподают под фильтр 41 | has_more_files = true 42 | end 43 | end} 44 | if last_fname then cmd(last_fname, not has_more_files) end 45 | end 46 | 47 | -- 48 | -- @param mask - если первый символ '!', то поиск производится рекурсивно 49 | -- @param days - количество дней после создания после которого файл считается старым 50 | -- @param cmd - команда обработки файла. В нее передается полный путь файла и 51 | -- признак того что файл является последним который подподает под маску в данной директории. 52 | -- Гарантируется что если это последний файл, то он имеет самую позднюю дату (самый "свежий") 53 | local function walk_old_files(mask, days, cmd) 54 | local is_recurse = mask:sub(1,1) == '!' 55 | 56 | if is_recurse then mask = mask:sub(2) end 57 | 58 | old_files_(mask, days, cmd) 59 | 60 | if not is_recurse then return end 61 | 62 | local dir_mask, file_mask = path.splitpath(mask) 63 | path.each{file = path.join(dir_mask, '*'), 64 | recurse=true, skipdirs=false, skipfiles=true, 65 | callback= function(fname) 66 | if path.isdir(fname) then 67 | old_files_(path.join(fname, file_mask), days, cmd) 68 | end 69 | end 70 | } 71 | end 72 | 73 | 74 | return walk_old_files -------------------------------------------------------------------------------- /lua/path/findfile.lua: -------------------------------------------------------------------------------- 1 | ------------------------------------------------------------------ 2 | -- 3 | -- Author: Alexey Melnichuk 4 | -- 5 | -- Copyright (C) 2013-2016 Alexey Melnichuk 6 | -- 7 | -- Licensed according to the included 'LICENCE' document 8 | -- 9 | -- This file is part of lua-path library. 10 | -- 11 | ------------------------------------------------------------------ 12 | 13 | --- 14 | -- Implementation of afx.findfile 15 | 16 | local string = require "string" 17 | local table = require "table" 18 | local coroutine = require "coroutine" 19 | local PATH = require "path.module" 20 | 21 | local function load(findfile_t) 22 | 23 | local function clone(t) local o = {} for k,v in pairs(t) do o[k] = v end return o end 24 | 25 | local function findfile_ssf(str_file, str_params, func_callback, tbl_option) 26 | tbl_option = tbl_option and clone(tbl_option) or {} 27 | tbl_option.file = assert(str_file) 28 | tbl_option.param = assert(str_params) 29 | tbl_option.callback = assert(func_callback) 30 | return findfile_t(tbl_option) 31 | end 32 | 33 | local function findfile_ss(str_file, str_params, tbl_option) 34 | tbl_option = tbl_option and clone(tbl_option) or {} 35 | tbl_option.file = assert(str_file) 36 | tbl_option.param = assert(str_params) 37 | return findfile_t(tbl_option) 38 | end 39 | 40 | local function findfile_sf(str_file, func_callback, tbl_option) 41 | tbl_option = tbl_option and clone(tbl_option) or {} 42 | tbl_option.file = assert(str_file) 43 | tbl_option.callback = assert(func_callback) 44 | return findfile_t(tbl_option) 45 | end 46 | 47 | local function findfile_s(str_file, tbl_option) 48 | tbl_option = tbl_option and clone(tbl_option) or {} 49 | tbl_option.file = assert(str_file) 50 | return findfile_t(tbl_option) 51 | end 52 | 53 | local function findfile_f(func_callback, tbl_option) 54 | tbl_option = clone(assert(tbl_option)) -- need file 55 | tbl_option.callback = assert(func_callback) 56 | return findfile_t(tbl_option) 57 | end 58 | 59 | local function findfile(p1,p2,p3,p4) 60 | if type(p1) == 'string' then 61 | if type(p2) == 'string' then 62 | if type(p3) == 'function' then 63 | return findfile_ssf(p1,p2,p3,p4) 64 | end 65 | return findfile_ss(p1,p2,p3) 66 | end 67 | if type(p2) == 'function' then 68 | return findfile_sf(p1,p2,p3) 69 | end 70 | return findfile_s(p1,p2) 71 | end 72 | if type(p1) == 'function' then 73 | return findfile_f(p1,p2) 74 | end 75 | return findfile_t(p1) 76 | end 77 | 78 | return findfile 79 | end 80 | 81 | return {load = load} -------------------------------------------------------------------------------- /test/each_perf.lua: -------------------------------------------------------------------------------- 1 | local TIMER = require "lzmq.timer".monotonic() 2 | local COUNT = 0 3 | local function counter_reset() COUNT = 0 end 4 | local function counter() COUNT = COUNT + 1 end 5 | 6 | local function etime(name, fn) 7 | counter_reset() 8 | TIMER:reset() 9 | TIMER:start() 10 | fn() 11 | local elapsed = TIMER:stop() 12 | io.write(name, ' - elapsed: ', elapsed, '(ms) counter: ', COUNT, '\n') 13 | end 14 | 15 | local function prequire(name) 16 | local ok, err = pcall(require, name) 17 | if ok then return err, name end 18 | return nil, err 19 | end 20 | 21 | local function load_winfs(...) 22 | local m = prequire"path.win32.fs" 23 | if not m then return end 24 | local ok, m = pcall(m.load, ...) 25 | if ok then return m end 26 | return nil, err 27 | end 28 | 29 | local function run(fs, P) 30 | etime('generic for', function() for f in fs.each(P,{recurse=true}) do counter() end end) 31 | etime(' callback', function() fs.each(P,counter,{recurse=true}) end) 32 | etime(' foreach', function() fs.foreach(P,counter,{recurse=true}) end) 33 | end 34 | 35 | if not (arg[1] and arg[2]) then 36 | print("usage: each_perf ") 37 | return 38 | end 39 | 40 | 41 | local DIR_SEP = package.config:sub(1,1) 42 | local base = arg[1] 43 | local mask = arg[2] 44 | 45 | if base:sub(-1) ~= DIR_SEP then 46 | base = base .. DIR_SEP 47 | end 48 | 49 | local lfs_each 50 | local lfs = prequire"lfs" 51 | if lfs then 52 | function lfs_each(P, cb) 53 | for name in lfs.dir(P) do if name == '.' or name == '..' then else 54 | local path = P .. DIR_SEP .. name 55 | if lfs.attributes(P, "mode") == "directory" then 56 | lfs_each(path, cb) 57 | end 58 | cb(path) 59 | end end 60 | end 61 | -- warmup file system 62 | lfs_each(base:sub(1,-2),counter) 63 | end 64 | 65 | if lfs then 66 | print("=============================================") 67 | print("== lfs.dir ==") 68 | print("without mask:") 69 | etime(" ", function() lfs_each(base:sub(1,-2), counter) end) 70 | end 71 | 72 | local afx = prequire"afx" 73 | if afx then 74 | print("=============================================") 75 | print("== afx.findfile ==") 76 | print("without mask:") 77 | etime(" ", function() 78 | afx.findfile{file = base .. "*",skipdirs=false;skipfiles=false;recurse=true;callback=counter} 79 | end) 80 | print("---------------------------------------------") 81 | print("with mask:") 82 | etime(" ", function() 83 | afx.findfile{file = base .. mask,skipdirs=false;skipfiles=false;recurse=true;callback=counter} 84 | end) 85 | end 86 | 87 | local fs = prequire "path.lfs.fs" 88 | if fs then 89 | print("=============================================") 90 | print("== path.lfs.fs ==") 91 | print("without mask:") 92 | run(fs, base) 93 | print("---------------------------------------------") 94 | print("with mask:") 95 | run(fs, base .. mask) 96 | end 97 | 98 | local fs = load_winfs("alien", "A") 99 | if fs then 100 | print("=============================================") 101 | print("== path.win32.alien.fs ==") 102 | print("without mask:") 103 | run(fs, base) 104 | print("with mask:") 105 | run(fs, base .. mask) 106 | end 107 | 108 | local fs = load_winfs("ffi", "A") 109 | if fs then 110 | print("=============================================") 111 | print("== path.win32.ffi.fs ==") 112 | print("without mask:") 113 | run(fs, base) 114 | print("with mask:") 115 | run(fs, base .. mask) 116 | end 117 | -------------------------------------------------------------------------------- /lua/path/win32/alien/types.lua: -------------------------------------------------------------------------------- 1 | ------------------------------------------------------------------ 2 | -- 3 | -- Author: Alexey Melnichuk 4 | -- 5 | -- Copyright (C) 2013-2016 Alexey Melnichuk 6 | -- 7 | -- Licensed according to the included 'LICENCE' document 8 | -- 9 | -- This file is part of lua-path library. 10 | -- 11 | ------------------------------------------------------------------ 12 | 13 | local alien = require "alien" 14 | local autil = require "path.win32.alien.utils" 15 | 16 | local function ztrim(str) 17 | local pos = 1 18 | while true do 19 | pos = string.find(str, "\000\000", pos, true) 20 | if not pos then return str end 21 | if 0 ~= (pos % 2) then return string.sub(str, 1, pos - 1) end 22 | pos = pos + 1 23 | end 24 | end 25 | 26 | assert( alien.sizeof("uint") == 4 ) 27 | assert( alien.sizeof("pointer") == alien.sizeof("size_t") ) 28 | 29 | local MAX_PATH = 260 30 | local CHAR = function(N) return "c" .. N end 31 | local DWORD = "I4" 32 | local ULONG = DWORD; 33 | local DEVICE_TYPE = DWORD; 34 | 35 | local FILETIME = autil.define_struct{ 36 | {DWORD, "dwLowDateTime" }; 37 | {DWORD, "dwHighDateTime" }; 38 | } 39 | 40 | local WIN32_FIND_DATAA = autil.define_struct{ 41 | { DWORD ,"dwFileAttributes" }; 42 | { FILETIME ,"ftCreationTime" }; 43 | { FILETIME ,"ftLastAccessTime" }; 44 | { FILETIME ,"ftLastWriteTime" }; 45 | { DWORD ,"nFileSizeHigh" }; 46 | { DWORD ,"nFileSizeLow" }; 47 | { DWORD ,"dwReserved0" }; 48 | { DWORD ,"dwReserved1" }; 49 | { CHAR(MAX_PATH) ,"cFileName" }; 50 | { CHAR(14) ,"cAlternateFileName" }; 51 | } 52 | 53 | local WIN32_FIND_DATAW = autil.define_struct{ 54 | { DWORD ,"dwFileAttributes" }; 55 | { FILETIME ,"ftCreationTime" }; 56 | { FILETIME ,"ftLastAccessTime" }; 57 | { FILETIME ,"ftLastWriteTime" }; 58 | { DWORD ,"nFileSizeHigh" }; 59 | { DWORD ,"nFileSizeLow" }; 60 | { DWORD ,"dwReserved0" }; 61 | { DWORD ,"dwReserved1" }; 62 | { CHAR(2*MAX_PATH),"cFileName" }; 63 | { CHAR(2*14) ,"cAlternateFileName" }; 64 | } 65 | 66 | local WIN32_FILE_ATTRIBUTE_DATA = function(s) return { 67 | dwFileAttributes = s.dwFileAttributes; 68 | ftCreationTime = {s.ftCreationTime.dwLowDateTime, s.ftCreationTime.dwHighDateTime}; 69 | ftLastAccessTime = {s.ftLastAccessTime.dwLowDateTime, s.ftLastAccessTime.dwHighDateTime}; 70 | ftLastWriteTime = {s.ftLastWriteTime.dwLowDateTime, s.ftLastWriteTime.dwHighDateTime}; 71 | nFileSize = {s.nFileSizeLow, s.nFileSizeHigh}; 72 | }end; 73 | 74 | local WIN32_FIND_DATAA2LUA = function(s) 75 | local res = WIN32_FILE_ATTRIBUTE_DATA(s) 76 | res.cFileName = s.cFileName:gsub("%z.*$", "") 77 | return res 78 | end; 79 | 80 | local WIN32_FIND_DATAW2LUA = function(s) 81 | local res = WIN32_FILE_ATTRIBUTE_DATA(s) 82 | res.cFileName = ztrim(s.cFileName) 83 | return res 84 | end; 85 | 86 | local STORAGE_DEVICE_NUMBER = autil.define_struct{ 87 | { DEVICE_TYPE , "DeviceType" }; 88 | { ULONG , "DeviceNumber" }; 89 | { ULONG , "PartitionNumber" }; 90 | } 91 | 92 | local c2lua = { 93 | WIN32_FIND_DATAA = WIN32_FIND_DATAA2LUA; 94 | WIN32_FIND_DATAW = WIN32_FIND_DATAW2LUA; 95 | } 96 | 97 | local CTYPES = { 98 | HANDLE = "size_t"; 99 | DWORD = "uint"; 100 | 101 | FILETIME = FILETIME; 102 | WIN32_FIND_DATAA = WIN32_FIND_DATAA; 103 | WIN32_FIND_DATAW = WIN32_FIND_DATAW; 104 | STORAGE_DEVICE_NUMBER = STORAGE_DEVICE_NUMBER; 105 | } 106 | 107 | return { 108 | CTYPES = CTYPES; 109 | CTYPE2LUA = c2lua; 110 | INVALID_HANDLE = autil.cast(-1, CTYPES.HANDLE) 111 | } -------------------------------------------------------------------------------- /doc/fs.ldoc: -------------------------------------------------------------------------------- 1 | --- 2 | -- 3 | -- This is lowlevel module which implement 4 | -- system dependent operation. 5 | --
Can use LuaFileSystem or alien/ffi on Windows 6 | -- @module path.fs 7 | 8 | local fs = {} 9 | 10 | --- The character used by the operating system to separate pathname components. 11 | -- 12 | fs.DIR_SEP = DIR_SEP 13 | 14 | --- Mask used to search all files in some dir by `fs.each`. 15 | -- 16 | fs.ANY_MASK = ANY_MASK 17 | 18 | --- Return current work directory path. 19 | -- 20 | function fs.currentdir() end 21 | 22 | --- Returns a table with the file attributes. 23 | -- 24 | -- @tparam string P 25 | -- @tparam[opt=nil] string name attribute name 26 | -- @treturn[1] table {attr1=value1, ...} 27 | -- @return[2] value attribute value if attribute name was specify 28 | function fs.attributes(P, name)end 29 | 30 | --- Return file attributes. 31 | -- 32 | -- On Windows it is result of GetFileAttributesEx 33 | function fs.flags(P) end 34 | 35 | --- Return creation file time. 36 | -- On *nix this is the time of the last metadata change. 37 | -- 38 | function fs.ctime(P)end 39 | 40 | --- Return last modification file time. 41 | -- 42 | function fs.mtime(P)end 43 | 44 | --- Return last access file time. 45 | -- 46 | function fs.atime(P)end 47 | 48 | --- Return size in bytes for file. 49 | -- 50 | function fs.size(P) end 51 | 52 | --- Return path if path existing in file system. 53 | -- 54 | function fs.exists(P) end 55 | 56 | --- Return path if path refers to an existing directory. 57 | -- 58 | function fs.isdir(P)end 59 | 60 | --- Return path if path refers to an existing file. 61 | -- 62 | function fs.isfile(P)end 63 | 64 | --- Return path if path refers to an existing symbolic link. 65 | -- 66 | function fs.islink(P)end 67 | 68 | --- Create new directory. 69 | -- 70 | -- This function can not create nested sub directories. 71 | -- 72 | function fs.mkdir(P) end 73 | 74 | --- Remove empty directory. 75 | -- 76 | function fs.rmdir(P) end 77 | 78 | --- Change current work directory path. 79 | -- 80 | function fs.chdir(P) end 81 | 82 | --- Copy one existed file. 83 | -- 84 | function fs.copy(src, dst, force) end 85 | 86 | --- Rename (move) existed file. 87 | -- 88 | function fs.move(src, dst, flags) end 89 | 90 | --- Remove file. 91 | -- 92 | function fs.remove(P) end 93 | 94 | --- Return path to system temp directory. 95 | -- 96 | function fs.tmpdir() end 97 | 98 | --- Set access and modification times of a file. 99 | -- 100 | function fs.touch(P, atime, ctime) end 101 | 102 | --- Implimentation of lfs.dir 103 | -- 104 | function fs.dir() end 105 | 106 | --- Simple non recursive iterator 107 | -- @local 108 | function fs.foreach(base, callback, option) end 109 | 110 | --- Implimentation of afx.findfile 111 | -- @tparam each_options option 112 | function fs.each(option) end 113 | 114 | --- ??? 115 | -- 116 | -- opt.file="./temp" -- match only this path 117 | --
opt.file="./temp/" -- match all files in this path 118 | --
This function do not expand filepath. So if we use "./*" mask and 'f' selector we get './some.file'. 119 | -- 120 | -- @table each_options 121 | -- @tfield[opt=fs.currentdir() .. fs.DIR_SEP .. fs.ANY_MASK] string file path and mask 122 | -- @tfield[opt="f"] string param 123 | -- - f fullpath and name 124 | -- - p path 125 | -- - n name 126 | -- - m mode 127 | -- - a attributes 128 | -- - z size 129 | -- - t mtime 130 | -- - c ctime 131 | -- - l atime 132 | -- @tfield[opt=false] boolean recurse include subdirs 133 | -- @tfield[opt=nil] boolean reverse direction of recursion 134 | -- - true subdirs at first 135 | -- - false subdirs at last 136 | -- - nil order undefined 137 | -- @tfield[opt=false] boolean delay if true then iterate over snapshot of directory 138 | -- @tfield[opt=false] boolean skipdirs 139 | -- @tfield[opt=false] boolean skipfiles 140 | -- @tfield[opt=true] boolean skipdots 141 | -- @tfield[opt=nil] function filter 142 | -- 143 | 144 | -------------------------------------------------------------------------------- /lua/path/win32/ffi/wcs.lua: -------------------------------------------------------------------------------- 1 | ------------------------------------------------------------------ 2 | -- 3 | -- Author: Alexey Melnichuk 4 | -- 5 | -- Copyright (C) 2013-2016 Alexey Melnichuk 6 | -- 7 | -- Licensed according to the included 'LICENCE' document 8 | -- 9 | -- This file is part of lua-path library. 10 | -- 11 | ------------------------------------------------------------------ 12 | 13 | local ffi = require("ffi") 14 | ffi.cdef[[ 15 | int __stdcall MultiByteToWideChar(unsigned int cp, uint32_t flag, const char* src, int srclen, wchar_t* dst, int dstlen); 16 | int __stdcall WideCharToMultiByte(unsigned int cp, uint32_t flag, const char* src, int srclen, char* dst, int dstlen, const char* defchar, int* used); 17 | uint32_t __stdcall GetLastError(void); 18 | ]] 19 | 20 | local C = ffi.C 21 | local achar_t = ffi.typeof'char[?]' 22 | local awchar_t = ffi.typeof'wchar_t[?]' 23 | local pchar_t = ffi.typeof'char*' 24 | local pwchar_t = ffi.typeof'wchar_t*' 25 | -- The data area passed to a system call is too small. 26 | local ERROR_INSUFFICIENT_BUFFER = 122; -- 0x0000007A 27 | 28 | local function strnlen(data, n) 29 | if type(data) == 'string' then 30 | return #data 31 | end 32 | if not n then 33 | if ffi.istype(pchar_t, data) then 34 | n = math.huge 35 | else -- assume char[?] / char&[...] 36 | n = assert(ffi.sizeof(data)) 37 | end 38 | end 39 | for i = 0, n-1 do 40 | if data[i] == 0 then return i end 41 | end 42 | 43 | return n 44 | end 45 | 46 | local function wcsnlen(data, n) 47 | if type(data) == 'string' then 48 | return math.ceil(#data/2) 49 | end 50 | 51 | if not n then 52 | if ffi.istype(pchar_t, data) then 53 | n = math.huge 54 | else -- assume wchar[?] / wchar&[...] 55 | n = math.ceil(assert(ffi.sizeof(data))/2) 56 | end 57 | end 58 | for i = 0, n-1 do 59 | if data[i] == 0 then return i end 60 | end 61 | 62 | return n 63 | end 64 | 65 | local function MultiByteToWideChar(src, cp) 66 | if not src or #src == 0 then return src, 0 end 67 | local flag = true 68 | local buflen = strnlen(src) 69 | local dst = ffi.new(awchar_t, buflen + 1) -- eos 70 | local ret = C.MultiByteToWideChar(cp, 0, src, #src, dst, buflen) 71 | if ret < 0 then return nil, C.GetLastError() end 72 | if ret <= buflen then 73 | dst[ret] = 0 74 | return dst, ret 75 | end 76 | dst = ffi.new(awchar_t, 1) 77 | dst[0] = 0 78 | return dst,0 79 | end 80 | 81 | local function WideCharToMultiByte(src, cp) 82 | local srclen = wcsnlen(src) 83 | local buflen = srclen + 1 84 | if type(src) == 'userdata' then src = ffi.cast('const char*', src) end 85 | while true do 86 | local dst = ffi.new("char[?]", buflen + 1) -- eof 87 | local ret = ffi.C.WideCharToMultiByte(cp, 0, src, srclen, dst, buflen, nil, nil) 88 | if ret <= 0 then 89 | local err = C.GetLastError() 90 | if err == ERROR_INSUFFICIENT_BUFFER then 91 | buflen = math.ceil(1.5 * buflen) 92 | else 93 | return nil, err 94 | end 95 | else 96 | if ret <= buflen then 97 | return dst, ret 98 | end 99 | end 100 | end 101 | local dst = ffi.new(achar_t, 1) 102 | dst[0] = 0 103 | return dst,0 104 | end 105 | 106 | local function LUA_W2M(src,...) 107 | if not src or #src == 0 then return src end 108 | local dst, dstlen = WideCharToMultiByte(src,...) 109 | if not dst then return nil, dstlen end 110 | return ffi.string(dst, dstlen) 111 | end 112 | 113 | local const_pchar_t = ffi.typeof'char*' 114 | local function LUA_M2W(src,...) 115 | if not src or #src == 0 then return src end 116 | local dst, dstlen = MultiByteToWideChar(src,...) 117 | if not dst then return nil, dstlen end 118 | return ffi.string(ffi.cast(const_pchar_t, dst), dstlen*2) 119 | end 120 | 121 | local _M = { 122 | MultiByteToWideChar = MultiByteToWideChar; 123 | WideCharToMultiByte = WideCharToMultiByte; 124 | mbstowcs = LUA_M2W; 125 | wcstombs = LUA_W2M; 126 | } 127 | 128 | return _M 129 | -------------------------------------------------------------------------------- /lua/path/win32/alien/wcs.lua: -------------------------------------------------------------------------------- 1 | ------------------------------------------------------------------ 2 | -- 3 | -- Author: Alexey Melnichuk 4 | -- 5 | -- Copyright (C) 2013-2016 Alexey Melnichuk 6 | -- 7 | -- Licensed according to the included 'LICENCE' document 8 | -- 9 | -- This file is part of lua-path library. 10 | -- 11 | ------------------------------------------------------------------ 12 | 13 | local alien = require "alien" 14 | local kernel32 = assert(alien.load("kernel32.dll")) 15 | local MultiByteToWideChar_ = assert(kernel32.MultiByteToWideChar) 16 | local WideCharToMultiByte_ = assert(kernel32.WideCharToMultiByte) 17 | local GetLastError = assert(kernel32.GetLastError) 18 | 19 | local DWORD = "uint" 20 | local WCHAR_SIZE = 2 21 | 22 | -- int __stdcall MultiByteToWideChar(UINT cp, DWORD flag, const char* src, int srclen, wchar_t* dst, int dstlen); 23 | MultiByteToWideChar_:types{abi="stdcall", ret = "int", 24 | "uint", -- cp 25 | DWORD, -- flag 26 | "string", -- src (const char*) 27 | "int", -- srclen 28 | "string", -- dst (wchar_t*) 29 | "int" -- dstlen 30 | } 31 | 32 | --int __stdcall WideCharToMultiByte(UINT cp, DWORD flag, const wchar_t* src, int srclen, char* dst, int dstlen, const char* defchar, int* used); 33 | WideCharToMultiByte_:types{abi="stdcall", ret = "int", 34 | "int", -- cp 35 | DWORD, -- flag 36 | "string", -- src (const wchar_t*) 37 | "int", -- srclen 38 | "string", -- dst (char*) 39 | "int", -- dstlen 40 | "pointer", -- defchar (char*) 41 | "pointer" -- used(int*) 42 | } 43 | 44 | GetLastError:types{ret = DWORD, abi='stdcall'} 45 | 46 | local function strnlen(data, n) 47 | if type(data) == 'string' then 48 | return #data 49 | end 50 | n = n or #data 51 | for i = 1, n do 52 | if data[i] == 0 then 53 | return i 54 | end 55 | end 56 | return n 57 | end 58 | 59 | local function wcsnlen(data, n) 60 | if type(data) == 'string' then 61 | return math.ceil(#data/2) 62 | end 63 | n = n or #data 64 | for i = 1, (2 * n), 2 do 65 | if (data[i] == 0) and (data[i+1] == 0) then 66 | return math.floor( i / 2 ) 67 | end 68 | end 69 | return n 70 | end 71 | 72 | local function MultiByteToWideChar(src, cp) 73 | local flag = true 74 | local buflen = strnlen(src) 75 | local dst = alien.buffer( WCHAR_SIZE * (buflen + 1) ) -- eos 76 | local ret = MultiByteToWideChar_(cp, 0, src, #src, dst, buflen) 77 | if ret < 0 then return nil, GetLastError() end 78 | if ret <= buflen then 79 | dst[ret * WCHAR_SIZE ] = 0 80 | dst[ret * WCHAR_SIZE + 1] = 0 81 | return dst, ret 82 | end 83 | dst = alien.buffer(WCHAR_SIZE * 1) 84 | dst[0] = 0 85 | dst[1] = 0 86 | return dst,0 87 | end 88 | 89 | local function WideCharToMultiByte(src, cp) 90 | local srclen = wcsnlen(src) 91 | local buflen = (srclen + 1) 92 | while true do 93 | local dst = alien.buffer(buflen + 1) -- eof 94 | local ret = WideCharToMultiByte_(cp, 0, src, srclen, dst, buflen, nil, nil) 95 | if ret <= 0 then 96 | local err = GetLastError() 97 | if err == 122 then -- buffer too small 98 | buflen = math.ceil(1.5 * buflen) 99 | else 100 | return nil, err 101 | end 102 | else 103 | if ret <= buflen then 104 | return dst, ret 105 | end 106 | end 107 | end 108 | local dst = alien.buffer(1) 109 | dst[0] = 0 110 | return dst, 0 111 | end 112 | 113 | local function LUA_M2W(src, ...) 114 | if not src or #src == 0 then return src end 115 | local dst, dstlen = MultiByteToWideChar(src, ...) 116 | if not dst then return nil, dstlen end 117 | return dst:tostring(dstlen * WCHAR_SIZE) 118 | end 119 | 120 | local function LUA_W2M(src, ...) 121 | if not src or #src == 0 then return src end 122 | local dst, dstlen = WideCharToMultiByte(src, ...) 123 | if not dst then return nil, dstlen end 124 | return dst:tostring(dstlen) 125 | end 126 | 127 | local _M = { 128 | MultiByteToWideChar = MultiByteToWideChar; 129 | WideCharToMultiByte = WideCharToMultiByte; 130 | mbstowcs = LUA_M2W; 131 | wcstombs = LUA_W2M; 132 | } 133 | 134 | return _M -------------------------------------------------------------------------------- /lua/path/win32/alien/utils.lua: -------------------------------------------------------------------------------- 1 | ------------------------------------------------------------------ 2 | -- 3 | -- Author: Alexey Melnichuk 4 | -- 5 | -- Copyright (C) 2013-2016 Alexey Melnichuk 6 | -- 7 | -- Licensed according to the included 'LICENCE' document 8 | -- 9 | -- This file is part of lua-path library. 10 | -- 11 | ------------------------------------------------------------------ 12 | 13 | local alien = require "alien" 14 | 15 | local STRUCT = {} 16 | STRUCT.__index = STRUCT 17 | 18 | local function define_struct(opt, t) 19 | if not t then t, opt = opt, nil end 20 | assert(type(t) == "table") 21 | assert(not opt or type(opt) == "table") 22 | 23 | local s_align = opt and opt.align or 1 24 | local off = 0 25 | local names, offsets, types,fields = {}, {}, {}, {} 26 | local fmt = "" 27 | 28 | for i, field in ipairs(t) do 29 | local ftype, fname 30 | local align = s_align 31 | if type(field) == "string" then 32 | ftype, fname = field, i 33 | elseif getmetatable(field) == STRUCT then 34 | ftype, fname = field, i 35 | else 36 | ftype, fname = field[1], field[2] or i 37 | align = field.align or align 38 | end 39 | off = math.ceil(off / align) * align 40 | table.insert(names, fname) 41 | offsets[fname] = off 42 | types[fname] = ftype 43 | if type(ftype) == "string" then 44 | off = off + alien.size(ftype) 45 | else 46 | off = off + ftype.size_ 47 | end 48 | end 49 | 50 | return setmetatable({ 51 | names_ = names, 52 | offsets_ = offsets, 53 | types_ = types, 54 | size_ = off, 55 | fmt_ = fmt, 56 | }, STRUCT) 57 | end 58 | 59 | function STRUCT:new(t, ptr) 60 | local buffer_ = alien.buffer(ptr or self.size_) 61 | 62 | local function get(_,key) 63 | local off = self.offsets_[key] 64 | if not off then error("field " .. key .. " does not exist") end 65 | local ftype = assert(self.types_[key]) 66 | 67 | if type(ftype) ~= "string" then 68 | local ptr = buffer_:topointer(off + 1) 69 | return ftype:new(nil, ptr) 70 | end 71 | 72 | local size = alien.size(ftype) 73 | local str = buffer_:tostring(size, off + 1) 74 | return alien.unpack(ftype, str) 75 | end 76 | 77 | local function set(_,key, val) 78 | local off = self.offsets_[key] 79 | if not off then error("field " .. key .. " does not exist") end 80 | local ftype = assert(self.types_[key]) 81 | local ptr = buffer_:topointer(off + 1) 82 | local size = alien.size(ftype) 83 | local val = alien.pack(ftype, val) 84 | alien.memmove( ptr, val, #val ) 85 | end 86 | 87 | local o = setmetatable({}, { 88 | __index = get; __newindex = set; 89 | __call = function () return buffer_ end 90 | }) 91 | 92 | if t then for k, v in pairs(t) do 93 | o[k] = v 94 | end end 95 | 96 | return o 97 | end 98 | 99 | local function self_struct_test() 100 | 101 | local S1 = define_struct{ 102 | {"I4", "s1v1"}; 103 | {"I4", "s1v2"}; 104 | } 105 | 106 | local S2 = define_struct{ 107 | {"I4", "s2v1"}; 108 | {"I4", "s2v2"}; 109 | } 110 | 111 | local SS = define_struct{ 112 | {S1, "s1"}; 113 | {S2, "s2"}; 114 | } 115 | 116 | local s = SS:new() 117 | alien.memset(s(),0, SS.size_) 118 | assert(s.s1.s1v1 == 0) 119 | assert(s.s1.s1v2 == 0) 120 | assert(s.s2.s2v1 == 0) 121 | assert(s.s2.s2v2 == 0) 122 | assert(not pcall(function() return s.s1.s2v1 end)) 123 | assert(not pcall(function() return s.s1.s1v3 end)) 124 | assert(not pcall(function() return s.s3.s3v1 end)) 125 | s.s1.s1v1 = 123 126 | s.s2.s2v1 = 456 127 | assert(s.s1.s1v1 == 123) 128 | assert(s.s1.s1v2 == 0) 129 | assert(s.s2.s2v1 == 456) 130 | assert(s.s2.s2v2 == 0) 131 | end 132 | 133 | self_struct_test() 134 | 135 | local function cast(v,t) 136 | local tmp = alien.buffer(alien.sizeof(t)) 137 | tmp:set(1, v, t) 138 | return tmp:get(1,t) 139 | end 140 | 141 | local gc_wrap, gc_null 142 | if _VERSION >= 'Lua 5.2' then 143 | local setmetatable = setmetatable 144 | gc_wrap = function(v, fn) 145 | return setmetatable({ 146 | value = v; 147 | }, { __gc = function() fn(v) end}) 148 | end 149 | 150 | gc_null = function(h) 151 | setmetatable(h, nil) 152 | return h.value 153 | end 154 | 155 | else 156 | local debug = require "debug" 157 | local newproxy = newproxy 158 | local assert = assert 159 | local setmetatable = setmetatable 160 | 161 | local function gc(fn) 162 | local p = assert(newproxy()) 163 | assert(debug.setmetatable(p, { __gc = fn })) 164 | return p 165 | end 166 | 167 | gc_wrap = function(v, fn) 168 | return { 169 | value = v; 170 | _ = gc(function() fn(v) end); 171 | } 172 | end 173 | 174 | gc_null = function(h) 175 | debug.setmetatable(h._, nil) 176 | return h.value 177 | end 178 | 179 | end 180 | 181 | local _M = { 182 | define_struct = define_struct; 183 | cast = cast; 184 | gc_wrap = gc_wrap; 185 | gc_null = gc_null; 186 | } 187 | 188 | return _M -------------------------------------------------------------------------------- /cmake/FindLua.cmake: -------------------------------------------------------------------------------- 1 | # Locate Lua library 2 | # This module defines 3 | # LUA_EXECUTABLE, if found 4 | # LUA_FOUND, if false, do not try to link to Lua 5 | # LUA_LIBRARIES 6 | # LUA_INCLUDE_DIR, where to find lua.h 7 | # LUA_VERSION_STRING, the version of Lua found (since CMake 2.8.8) 8 | # 9 | # Note that the expected include convention is 10 | # #include "lua.h" 11 | # and not 12 | # #include 13 | # This is because, the lua location is not standardized and may exist 14 | # in locations other than lua/ 15 | 16 | #============================================================================= 17 | # Copyright 2007-2009 Kitware, Inc. 18 | # Modified to support Lua 5.2 by LuaDist 2012 19 | # 20 | # Distributed under the OSI-approved BSD License (the "License"); 21 | # see accompanying file Copyright.txt for details. 22 | # 23 | # This software is distributed WITHOUT ANY WARRANTY; without even the 24 | # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 25 | # See the License for more information. 26 | #============================================================================= 27 | # (To distribute this file outside of CMake, substitute the full 28 | # License text for the above reference.) 29 | # 30 | # The required version of Lua can be specified using the 31 | # standard syntax, e.g. FIND_PACKAGE(Lua 5.1) 32 | # Otherwise the module will search for any available Lua implementation 33 | 34 | # Always search for non-versioned lua first (recommended) 35 | SET(_POSSIBLE_LUA_INCLUDE include include/lua) 36 | SET(_POSSIBLE_LUA_EXECUTABLE lua) 37 | SET(_POSSIBLE_LUA_LIBRARY lua) 38 | 39 | # Determine possible naming suffixes (there is no standard for this) 40 | IF(Lua_FIND_VERSION_MAJOR AND Lua_FIND_VERSION_MINOR) 41 | SET(_POSSIBLE_SUFFIXES "${Lua_FIND_VERSION_MAJOR}${Lua_FIND_VERSION_MINOR}" "${Lua_FIND_VERSION_MAJOR}.${Lua_FIND_VERSION_MINOR}" "-${Lua_FIND_VERSION_MAJOR}.${Lua_FIND_VERSION_MINOR}") 42 | ELSE(Lua_FIND_VERSION_MAJOR AND Lua_FIND_VERSION_MINOR) 43 | SET(_POSSIBLE_SUFFIXES "52" "5.2" "-5.2" "51" "5.1" "-5.1") 44 | ENDIF(Lua_FIND_VERSION_MAJOR AND Lua_FIND_VERSION_MINOR) 45 | 46 | # Set up possible search names and locations 47 | FOREACH(_SUFFIX ${_POSSIBLE_SUFFIXES}) 48 | LIST(APPEND _POSSIBLE_LUA_INCLUDE "include/lua${_SUFFIX}") 49 | LIST(APPEND _POSSIBLE_LUA_EXECUTABLE "lua${_SUFFIX}") 50 | LIST(APPEND _POSSIBLE_LUA_LIBRARY "lua${_SUFFIX}") 51 | ENDFOREACH(_SUFFIX) 52 | 53 | # Find the lua executable 54 | FIND_PROGRAM(LUA_EXECUTABLE 55 | NAMES ${_POSSIBLE_LUA_EXECUTABLE} 56 | ) 57 | 58 | # Find the lua header 59 | FIND_PATH(LUA_INCLUDE_DIR lua.h 60 | HINTS 61 | $ENV{LUA_DIR} 62 | PATH_SUFFIXES ${_POSSIBLE_LUA_INCLUDE} 63 | PATHS 64 | ~/Library/Frameworks 65 | /Library/Frameworks 66 | /usr/local 67 | /usr 68 | /sw # Fink 69 | /opt/local # DarwinPorts 70 | /opt/csw # Blastwave 71 | /opt 72 | ) 73 | 74 | # Find the lua library 75 | FIND_LIBRARY(LUA_LIBRARY 76 | NAMES ${_POSSIBLE_LUA_LIBRARY} 77 | HINTS 78 | $ENV{LUA_DIR} 79 | PATH_SUFFIXES lib64 lib 80 | PATHS 81 | ~/Library/Frameworks 82 | /Library/Frameworks 83 | /usr/local 84 | /usr 85 | /sw 86 | /opt/local 87 | /opt/csw 88 | /opt 89 | ) 90 | 91 | IF(LUA_LIBRARY) 92 | # include the math library for Unix 93 | IF(UNIX AND NOT APPLE) 94 | FIND_LIBRARY(LUA_MATH_LIBRARY m) 95 | SET( LUA_LIBRARIES "${LUA_LIBRARY};${LUA_MATH_LIBRARY}" CACHE STRING "Lua Libraries") 96 | # For Windows and Mac, don't need to explicitly include the math library 97 | ELSE(UNIX AND NOT APPLE) 98 | SET( LUA_LIBRARIES "${LUA_LIBRARY}" CACHE STRING "Lua Libraries") 99 | ENDIF(UNIX AND NOT APPLE) 100 | ENDIF(LUA_LIBRARY) 101 | 102 | # Determine Lua version 103 | IF(LUA_INCLUDE_DIR AND EXISTS "${LUA_INCLUDE_DIR}/lua.h") 104 | FILE(STRINGS "${LUA_INCLUDE_DIR}/lua.h" lua_version_str REGEX "^#define[ \t]+LUA_RELEASE[ \t]+\"Lua .+\"") 105 | 106 | STRING(REGEX REPLACE "^#define[ \t]+LUA_RELEASE[ \t]+\"Lua ([^\"]+)\".*" "\\1" LUA_VERSION_STRING "${lua_version_str}") 107 | UNSET(lua_version_str) 108 | ENDIF() 109 | 110 | # Lua 5.2 111 | IF(NOT LUA_VERSION_STRING) 112 | FILE(STRINGS "${LUA_INCLUDE_DIR}/lua.h" lua_version_str REGEX "^#define LUA_VERSION_[A-Z]+[ \t]+\"[0-9]+\"") 113 | STRING(REGEX REPLACE ".*#define LUA_VERSION_MAJOR[ \t]+\"([0-9]+)\".*" "\\1" LUA_VERSION_MAJOR ${lua_version_str}) 114 | STRING(REGEX REPLACE ".*#define LUA_VERSION_MINOR[ \t]+\"([0-9]+)\".*" "\\1" LUA_VERSION_MINOR ${lua_version_str}) 115 | STRING(REGEX REPLACE ".*#define LUA_VERSION_RELEASE[ \t]+\"([0-9]+)\".*" "\\1" LUA_VERSION_RELEASE ${lua_version_str}) 116 | SET(LUA_VERSION_STRING ${LUA_VERSION_MAJOR}.${LUA_VERSION_MINOR}.${LUA_VERSION_RELEASE}) 117 | ENDIF() 118 | 119 | INCLUDE(FindPackageHandleStandardArgs) 120 | # handle the QUIETLY and REQUIRED arguments and set LUA_FOUND to TRUE if 121 | # all listed variables are TRUE 122 | FIND_PACKAGE_HANDLE_STANDARD_ARGS(Lua 123 | REQUIRED_VARS LUA_LIBRARIES LUA_INCLUDE_DIR 124 | VERSION_VAR LUA_VERSION_STRING) 125 | 126 | MARK_AS_ADVANCED(LUA_INCLUDE_DIR LUA_LIBRARIES LUA_LIBRARY LUA_MATH_LIBRARY LUA_EXECUTABLE) 127 | 128 | -------------------------------------------------------------------------------- /lua/path/win32/ffi/types.lua: -------------------------------------------------------------------------------- 1 | ------------------------------------------------------------------ 2 | -- 3 | -- Author: Alexey Melnichuk 4 | -- 5 | -- Copyright (C) 2013-2016 Alexey Melnichuk 6 | -- 7 | -- Licensed according to the included 'LICENCE' document 8 | -- 9 | -- This file is part of lua-path library. 10 | -- 11 | ------------------------------------------------------------------ 12 | 13 | local ffi = require "ffi" 14 | 15 | local C = ffi.C 16 | 17 | local function pcdef(...) 18 | local ok, err = pcall( ffi.cdef, ... ) 19 | if not ok then return nil, err end 20 | return err 21 | end 22 | 23 | local function pack(n, str) 24 | return [[ 25 | #pragma pack(push) 26 | #pragma pack(1) 27 | ]] .. str ..[[ 28 | #pragma pack(pop) 29 | ]] 30 | end 31 | 32 | local function ztrim(str) 33 | local pos = 1 34 | while true do 35 | pos = string.find(str, "\000\000", pos, true) 36 | if not pos then return str end 37 | if 0 ~= (pos % 2) then return string.sub(str, 1, pos - 1) end 38 | pos = pos + 1 39 | end 40 | end 41 | 42 | ffi.cdef [[ 43 | static const int MAX_PATH = 260; 44 | typedef uint32_t DWORD; 45 | typedef char CHAR; 46 | typedef wchar_t WCHAR; 47 | typedef uint32_t DEVICE_TYPE; 48 | typedef uint32_t ULONG; 49 | typedef void* HANDLE; 50 | typedef void* HLOCAL; 51 | typedef void* LPVOID; 52 | typedef uint32_t BOOL; 53 | ]] 54 | 55 | pcdef(pack(1, [[ // GET_FILEEX_INFO_LEVELS 56 | typedef enum _GET_FILEEX_INFO_LEVELS { 57 | GetFileExInfoStandard, 58 | GetFileExMaxInfoLevel 59 | } GET_FILEEX_INFO_LEVELS; 60 | ]])) 61 | 62 | pcdef(pack(1, [[ // FILETIME 63 | typedef struct _FILETIME { 64 | DWORD dwLowDateTime; 65 | DWORD dwHighDateTime; 66 | } FILETIME, *PFILETIME; 67 | ]])) 68 | 69 | pcdef(pack(1, [[ // WIN32_FILE_ATTRIBUTE_DATA 70 | typedef struct _WIN32_FILE_ATTRIBUTE_DATA { 71 | DWORD dwFileAttributes; 72 | FILETIME ftCreationTime; 73 | FILETIME ftLastAccessTime; 74 | FILETIME ftLastWriteTime; 75 | DWORD nFileSizeHigh; 76 | DWORD nFileSizeLow; 77 | } WIN32_FILE_ATTRIBUTE_DATA, *PWIN32_FILE_ATTRIBUTE_DATA; 78 | ]])) 79 | 80 | pcdef(pack(1, [[ // WIN32_FIND_DATAA 81 | typedef struct _WIN32_FIND_DATAA { 82 | DWORD dwFileAttributes; 83 | FILETIME ftCreationTime; 84 | FILETIME ftLastAccessTime; 85 | FILETIME ftLastWriteTime; 86 | DWORD nFileSizeHigh; 87 | DWORD nFileSizeLow; 88 | DWORD dwReserved0; 89 | DWORD dwReserved1; 90 | CHAR cFileName[MAX_PATH]; 91 | CHAR cAlternateFileName[14]; 92 | } WIN32_FIND_DATAA, *PWIN32_FIND_DATAA; 93 | ]])) 94 | 95 | pcdef(pack(1, [[ // WIN32_FIND_DATAW 96 | typedef struct _WIN32_FIND_DATAW { 97 | DWORD dwFileAttributes; 98 | FILETIME ftCreationTime; 99 | FILETIME ftLastAccessTime; 100 | FILETIME ftLastWriteTime; 101 | DWORD nFileSizeHigh; 102 | DWORD nFileSizeLow; 103 | DWORD dwReserved0; 104 | DWORD dwReserved1; 105 | WCHAR cFileName[MAX_PATH]; 106 | WCHAR cAlternateFileName[14]; 107 | } WIN32_FIND_DATAW, *PWIN32_FIND_DATAW; 108 | ]])) 109 | 110 | pcdef(pack(1, [[ // STORAGE_DEVICE_NUMBER 111 | typedef struct _STORAGE_DEVICE_NUMBER { 112 | DEVICE_TYPE DeviceType; 113 | ULONG DeviceNumber; 114 | ULONG PartitionNumber; 115 | } STORAGE_DEVICE_NUMBER, *PSTORAGE_DEVICE_NUMBER; 116 | ]])) 117 | 118 | local CTYPES = { 119 | DWORD = ffi.typeof("DWORD"); 120 | PCHAR = ffi.typeof("CHAR*"); 121 | PWCHAR = ffi.typeof("WCHAR*"); 122 | VLA_CHAR = ffi.typeof("CHAR[?]"); 123 | VLA_WCHAR = ffi.typeof("WCHAR[?]"); 124 | 125 | WIN32_FILE_ATTRIBUTE_DATA = ffi.typeof("WIN32_FILE_ATTRIBUTE_DATA"); 126 | FILETIME = ffi.typeof("FILETIME"); 127 | WIN32_FIND_DATAA = ffi.typeof("WIN32_FIND_DATAA"); 128 | WIN32_FIND_DATAW = ffi.typeof("WIN32_FIND_DATAW"); 129 | STORAGE_DEVICE_NUMBER = ffi.typeof("STORAGE_DEVICE_NUMBER"); 130 | } 131 | 132 | local c2lua 133 | c2lua = { 134 | 135 | WIN32_FILE_ATTRIBUTE_DATA = function(s) return { 136 | dwFileAttributes = s.dwFileAttributes; 137 | ftCreationTime = {s.ftCreationTime.dwLowDateTime, s.ftCreationTime.dwHighDateTime}; 138 | ftLastAccessTime = {s.ftLastAccessTime.dwLowDateTime, s.ftLastAccessTime.dwHighDateTime}; 139 | ftLastWriteTime = {s.ftLastWriteTime.dwLowDateTime, s.ftLastWriteTime.dwHighDateTime}; 140 | nFileSize = {s.nFileSizeLow, s.nFileSizeHigh}; 141 | }end; 142 | 143 | WIN32_FIND_DATAA = function(s) 144 | local res = c2lua.WIN32_FILE_ATTRIBUTE_DATA(s) 145 | res.cFileName = ffi.string(s.cFileName); 146 | return res 147 | end; 148 | 149 | WIN32_FIND_DATAW = function(s) 150 | local res = c2lua.WIN32_FILE_ATTRIBUTE_DATA(s) 151 | local pstr = ffi.cast(CTYPES.PCHAR, s.cFileName) 152 | local str = ffi.string(pstr, C.MAX_PATH * 2) 153 | res.cFileName = ztrim(str) 154 | return res 155 | end; 156 | 157 | } 158 | 159 | local _M = { 160 | INVALID_HANDLE = ffi.cast("void*", -1); 161 | CTYPES = CTYPES; 162 | CTYPE2LUA = c2lua; 163 | } 164 | 165 | return _M 166 | -------------------------------------------------------------------------------- /test/test_each.lua: -------------------------------------------------------------------------------- 1 | local lunit = require "lunit" 2 | local TEST_CASE = lunit.TEST_CASE 3 | 4 | local SKIP_CASE 5 | if lunit.skip then 6 | SKIP_CASE = function(msg) return function() lunit.skip(msg) end end 7 | else 8 | SKIP_CASE = require "utils".skip 9 | end 10 | 11 | local path = require "path" 12 | local ISW = path.IS_WINDOWS 13 | 14 | local function prequire(...) 15 | local ok, mod = pcall(require, ...) 16 | if not ok then return nil, mod end 17 | return mod 18 | end 19 | 20 | local function mkfile(P, data) 21 | P = path.fullpath(P) 22 | path.mkdir(path.dirname(P)) 23 | local f, e = io.open(P, "w+b") 24 | if not f then return nil, err end 25 | if data then assert(f:write(data)) end 26 | f:close() 27 | return P 28 | end 29 | 30 | local function up(str) 31 | return path.IS_WINDOWS and str:upper() or str 32 | end 33 | 34 | local function clone(t, o) 35 | o = o or {} 36 | for k,v in pairs(t) do 37 | o[ k ] = v 38 | end 39 | return o 40 | end 41 | 42 | local function make_test(_ENV, opt) 43 | 44 | if setfenv then setfenv(1, _ENV) end 45 | 46 | local cwd, files, dirs, path_each 47 | 48 | function teardown() 49 | collectgarbage("collect") collectgarbage("collect") -- force clean lfs.dir 50 | path.remove(path.join(cwd, '1', '2', '3', 'test.dat')) 51 | path.remove(path.join(cwd, '1', '2', '3', 'test.txt')) 52 | path.remove(path.join(cwd, '1', '2', '3', 'file.dat')) 53 | path.rmdir(path.join(cwd, '1', '2', '3')) 54 | path.rmdir(path.join(cwd, '1', '2')) 55 | path.rmdir(path.join(cwd, '1')) 56 | end 57 | 58 | function setup() 59 | cwd = assert_string(path.currentdir()) 60 | teardown() 61 | path.mkdir(path.join(cwd, '1', '2', '3')) 62 | mkfile(path.join(cwd, '1', '2', '3', 'test.dat'), '12345') 63 | mkfile(path.join(cwd, '1', '2', '3', 'test.txt'), '12345') 64 | mkfile(path.join(cwd, '1', '2', '3', 'file.dat'), '12345') 65 | 66 | local findfile_t = assert(opt.get_findfile()) 67 | path_each = require "path.findfile".load(function(opt) 68 | opt.file = path.fullpath(opt.file) 69 | return findfile_t(opt) 70 | end) 71 | 72 | files = { 73 | [ up(path.join(cwd, '1', '2', '3', 'test.dat')) ] = true; 74 | [ up(path.join(cwd, '1', '2', '3', 'test.txt')) ] = true; 75 | [ up(path.join(cwd, '1', '2', '3', 'file.dat')) ] = true; 76 | } 77 | 78 | dirs = { 79 | [ up(path.join(cwd, '1', '2', '3')) ] = true; 80 | [ up(path.join(cwd, '1', '2')) ] = true; 81 | [ up(path.join(cwd, '1' )) ] = true; 82 | } 83 | end 84 | 85 | function test_cwd() 86 | assert_equal(cwd, path.fullpath(".")) 87 | end 88 | 89 | function test_attr() 90 | for P in pairs(files)do assert(path.exists(P)) end 91 | for P in pairs(files)do assert(path.isfile(P)) end 92 | for P in pairs(files)do assert_equal(5, path.size(P)) end 93 | 94 | local ts = os.time() + 100 95 | path_each("./1/*", function(f) 96 | assert(path.isfile(f)) 97 | assert(path.touch(f, ts)) 98 | end, {skipdirs=true, recurse=true}) 99 | 100 | path_each("./1/*", "ft", function(f,mt) 101 | local delta = math.abs(ts - mt) 102 | assert(delta <= 2) 103 | end, {skipdirs=true, recurse=true}) 104 | end 105 | 106 | function test_findfile() 107 | local params 108 | 109 | params = clone(files) 110 | path_each("./1/2/3/*.*", function(f) 111 | f = up(f) 112 | assert_not_nil(params[f], "unexpected: " .. f) 113 | params[f] = nil 114 | end) 115 | assert_nil(next(params)) 116 | 117 | params = clone(files) 118 | for f in path_each("./1/2/3/*.*") do 119 | f = up(f) 120 | assert_not_nil(params[f], "unexpected: " .. f) 121 | params[f] = nil 122 | end 123 | assert_nil(next(params)) 124 | 125 | params = clone(files) 126 | params = clone(dirs,params) 127 | path_each("./1/*", function(f) 128 | f = up(f) 129 | assert_not_nil(params[f], "unexpected: " .. f) 130 | params[f] = nil 131 | end, {recurse=true}) 132 | assert_equal(up(path.join(cwd, '1' )), next(params)) 133 | assert_nil(next(params, up(path.join(cwd, '1' )))) 134 | 135 | params = clone(files) 136 | path_each("./1/2/3/*.*", "fz", function(f, sz) 137 | f = up(f) 138 | assert_not_nil(params[f], "unexpected: " .. f) 139 | assert_equal(5, sz) 140 | params[f] = nil 141 | end) 142 | assert_nil(next(params)) 143 | 144 | params = clone(files) 145 | for f, sz in path_each("./1/2/3/*.*", "fz") do 146 | f = up(f) 147 | assert_not_nil(params[f], "unexpected: " .. f) 148 | assert_equal(5, sz) 149 | params[f] = nil 150 | end 151 | assert_nil(next(params)) 152 | 153 | params = clone(dirs) 154 | path_each("./*", "fzm", function(f, sz, m) 155 | f = up(f) 156 | assert_not_nil(params[f], "unexpected: " .. f) 157 | if ISW then assert_equal(0, sz) end 158 | assert_equal('directory', m) 159 | params[f] = nil 160 | end, {skipfiles=true, recurse=true}) 161 | assert_nil(next(params)) 162 | 163 | end 164 | 165 | function test_findfile_mask() 166 | params = clone(files) 167 | path_each("./1/2/3/t*.*", function(f) 168 | f = up(f) 169 | assert_not_nil(params[f], "unexpected: " .. f) 170 | params[f] = nil 171 | end) 172 | assert_not_nil(next(params)) 173 | end 174 | 175 | function test_findfile_break() 176 | local flag = false 177 | path_each("./1/2/3/*.*", function() 178 | assert_false(flag) 179 | flag = true 180 | return 'break' 181 | end) 182 | assert_true(flag) 183 | end 184 | 185 | end 186 | 187 | local _ENV = TEST_CASE('each lfs') 188 | if not prequire"lfs" then test = SKIP_CASE"lfs module not found" else 189 | make_test(_M or _ENV, { 190 | get_findfile = function() return require "path.lfs.fs".each_impl end; 191 | }) 192 | end 193 | 194 | local _ENV = TEST_CASE('each ffi') 195 | if not ISW then test = SKIP_CASE"ffi support only on Windows" 196 | elseif not prequire"ffi" then test = SKIP_CASE"ffi module not found" else 197 | make_test(_M or _ENV, { 198 | get_findfile = function() 199 | return require "path.win32.fs".load("ffi", "A").each_impl 200 | end; 201 | }) 202 | end 203 | 204 | local _ENV = TEST_CASE('each alien') 205 | if not ISW then test = SKIP_CASE"alien support only on Windows" 206 | elseif not prequire"alien" then test = SKIP_CASE"alien module not found" else 207 | make_test(_M or _ENV, { 208 | get_findfile = function() 209 | return require "path.win32.fs".load("alien", "A").each_impl 210 | end; 211 | }) 212 | end 213 | 214 | local _ENV = TEST_CASE('each syscall') 215 | if ISW then test = SKIP_CASE"syscall support only on non Windows" 216 | elseif not prequire"path.syscall.fs" then test = SKIP_CASE"syscall module not found" else 217 | make_test(_M or _ENV, { 218 | get_findfile = function() return require "path.syscall.fs".each_impl end; 219 | }) 220 | end 221 | 222 | if not LUNIT_RUN then lunit.run() end -------------------------------------------------------------------------------- /test/test_lfs.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env lua5.1 2 | 3 | local IS_WINDOWS = package.config:sub(1,1) == '\\' 4 | 5 | local function prequire(...) 6 | local ok, mod = pcall(require, ...) 7 | if not ok then return nil, mod end 8 | return mod 9 | end 10 | 11 | local function test(name, lfs) 12 | 13 | local nonexists = "/8f00e678b1984de4a49d7650e1534327" 14 | local sep = string.match (package.config, "[^\n]+") 15 | local upper = ".." 16 | 17 | print (name) 18 | 19 | io.write(".") 20 | io.flush() 21 | 22 | function attrdir (path) 23 | for file in lfs.dir(path) do 24 | if file ~= "." and file ~= ".." then 25 | local f = path..sep..file 26 | print ("\t=> "..f.." <=") 27 | local attr = lfs.attributes (f) 28 | assert (type(attr) == "table") 29 | if attr.mode == "directory" then 30 | attrdir (f) 31 | else 32 | for name, value in pairs(attr) do 33 | print (name, value) 34 | end 35 | end 36 | end 37 | end 38 | end 39 | 40 | -- Checking changing directories 41 | local current = assert (lfs.currentdir()) 42 | local reldir = string.gsub (current, "^.*%"..sep.."([^"..sep.."])$", "%1") 43 | assert (lfs.chdir (upper), "could not change to upper directory") 44 | assert (lfs.chdir (reldir), "could not change back to current directory") 45 | assert (lfs.currentdir() == current, "error trying to change directories") 46 | assert (lfs.chdir ("this couldn't be an actual directory") == nil, "could change to a non-existent directory") 47 | 48 | io.write(".") 49 | io.flush() 50 | 51 | -- Changing creating and removing directories 52 | local tmpdir = current..sep.."lfs_tmp_dir" 53 | local tmpfile = tmpdir..sep.."tmp_file" 54 | 55 | -- Test for existence of a previous lfs_tmp_dir 56 | -- that may have resulted from an interrupted test execution and remove it 57 | if lfs.chdir (tmpdir) then 58 | assert (lfs.chdir (upper), "could not change to upper directory") 59 | assert (os.remove (tmpfile), "could not remove file from previous test") 60 | assert (lfs.rmdir (tmpdir), "could not remove directory from previous test") 61 | end 62 | 63 | io.write(".") 64 | io.flush() 65 | 66 | -- tries to create a directory 67 | assert (lfs.mkdir (tmpdir), "could not make a new directory") 68 | local attrib, errmsg = lfs.attributes (tmpdir) 69 | if not attrib then 70 | error ("could not get attributes of file `"..tmpdir.."':\n"..errmsg) 71 | end 72 | local f = io.open(tmpfile, "w") 73 | f:close() 74 | 75 | io.write(".") 76 | io.flush() 77 | 78 | -- Change access time 79 | local testdate = os.time({ year = 2007, day = 10, month = 2, hour=0}) 80 | assert (lfs.touch (tmpfile, testdate)) 81 | local new_att = assert (lfs.attributes (tmpfile)) 82 | assert (math.abs(new_att.access - testdate) <= 1, "could not set access time") 83 | assert (math.abs(new_att.modification - testdate) <= 1, "could not set modification time") 84 | 85 | io.write(".") 86 | io.flush() 87 | 88 | -- Change access and modification time 89 | local testdate1 = os.time({ year = 2007, day = 10, month = 2, hour=0}) 90 | local testdate2 = os.time({ year = 2007, day = 11, month = 2, hour=0}) 91 | 92 | assert (lfs.touch (tmpfile, testdate2, testdate1)) 93 | local new_att = assert (lfs.attributes (tmpfile)) 94 | assert (math.abs(new_att.access - testdate2) <= 1, "could not set access time") 95 | assert (math.abs(new_att.modification - testdate1) <= 1, "could not set modification time") 96 | 97 | io.write(".") 98 | io.flush() 99 | 100 | -- Checking link (does not work on Windows) 101 | if lfs.link and lfs.link (tmpfile, "_a_link_for_test_", true) then 102 | assert (lfs.attributes"_a_link_for_test_".mode == "file") 103 | if lfs.symlinkattributes then 104 | assert (lfs.symlinkattributes"_a_link_for_test_".mode == "link") 105 | end 106 | assert (lfs.link (tmpfile, "_a_hard_link_for_test_")) 107 | assert (lfs.attributes (tmpfile, "nlink") == 2) 108 | assert (os.remove"_a_link_for_test_") 109 | assert (os.remove"_a_hard_link_for_test_") 110 | end 111 | 112 | io.write(".") 113 | io.flush() 114 | 115 | -- -- Checking text/binary modes (only has an effect in Windows) 116 | -- local f = io.open(tmpfile, "w") 117 | -- local result, mode = lfs.setmode(f, "binary") 118 | -- assert(result) -- on non-Windows platforms, mode is always returned as "binary" 119 | -- result, mode = lfs.setmode(f, "text") 120 | -- assert(result and mode == "binary") 121 | -- f:close() 122 | -- 123 | -- io.write(".") 124 | -- io.flush() 125 | 126 | -- Restore access time to current value 127 | assert (lfs.touch (tmpfile, attrib.access, attrib.modification)) 128 | new_att = assert (lfs.attributes (tmpfile)) 129 | assert (math.abs(new_att.access - attrib.access) <= 1) 130 | assert (math.abs(new_att.modification - attrib.modification) <= 1) 131 | 132 | io.write(".") 133 | io.flush() 134 | 135 | -- Remove new file and directory 136 | assert (os.remove (tmpfile), "could not remove new file") 137 | assert (lfs.rmdir (tmpdir), "could not remove new directory") 138 | assert (lfs.mkdir (tmpdir..sep.."lfs_tmp_dir") == nil, "could create a directory inside a non-existent one") 139 | 140 | io.write(".") 141 | io.flush() 142 | 143 | -- Trying to get attributes of a non-existent file 144 | assert (lfs.attributes ("this couldn't be an actual file") == nil, "could get attributes of a non-existent file") 145 | assert (type(lfs.attributes (upper)) == "table", "couldn't get attributes of upper directory") 146 | 147 | io.write(".") 148 | io.flush() 149 | 150 | -- Stressing directory iterator (nonexists) 151 | if IS_WINDOWS then 152 | count = 0 153 | for i = 1, 4000 do 154 | for file in lfs.dir (nonexists) do 155 | count = count + 1 156 | end 157 | end 158 | 159 | io.write(".") 160 | io.flush() 161 | end 162 | 163 | -- Stressing directory iterator (exists) 164 | count = 0 165 | for i = 1, 4000 do 166 | for file in lfs.dir (current) do 167 | count = count + 1 168 | end 169 | end 170 | 171 | io.write(".") 172 | io.flush() 173 | 174 | -- Stressing directory iterator, explicit version (nonexists) 175 | if IS_WINDOWS then 176 | count = 0 177 | for i = 1, 4000 do 178 | local iter, dir = lfs.dir(nonexists) 179 | local file = dir:next() 180 | while file do 181 | count = count + 1 182 | file = dir:next() 183 | end 184 | assert(not pcall(dir.next, dir)) 185 | end 186 | 187 | io.write(".") 188 | io.flush() 189 | end 190 | 191 | -- Stressing directory iterator, explicit version (exists) 192 | count = 0 193 | for i = 1, 4000 do 194 | local iter, dir = lfs.dir(current) 195 | local file = dir:next() 196 | while file do 197 | count = count + 1 198 | file = dir:next() 199 | end 200 | assert(not pcall(dir.next, dir)) 201 | end 202 | 203 | io.write(".") 204 | io.flush() 205 | 206 | if IS_WINDOWS then 207 | -- directory explicit close 208 | local iter, dir = lfs.dir(nonexists) 209 | dir:close() 210 | assert(not pcall(dir.next, dir)) 211 | 212 | io.write(".") 213 | io.flush() 214 | end 215 | 216 | -- directory explicit close 217 | local iter, dir = lfs.dir(current) 218 | dir:close() 219 | assert(not pcall(dir.next, dir)) 220 | print"Ok!" 221 | 222 | end 223 | 224 | local pass = true 225 | 226 | local function run_test(...) 227 | local ok, err = pcall(test, ...) 228 | if not ok then 229 | print() 230 | print(err) 231 | end 232 | pass = ok and pass 233 | print("----------------------------------------------------") 234 | end 235 | 236 | if IS_WINDOWS then 237 | if prequire"alien" then 238 | local lfs = require"path.win32.fs".load("alien", "A") 239 | run_test("lfs.alienA", lfs) 240 | end 241 | 242 | if prequire"ffi" then 243 | local lfs = require"path.win32.fs".load("ffi", "A") 244 | run_test("lfs.ffiA", lfs) 245 | end 246 | else 247 | if prequire"path.syscall.fs" then 248 | local lfs = prequire"path.syscall.fs" 249 | run_test("lfs.syscall", lfs) 250 | end 251 | end 252 | 253 | if prequire"path.lfs.fs" then 254 | local lfs = prequire"path.lfs.fs" 255 | run_test("lfs.lfs", lfs) 256 | end 257 | 258 | if not pass then os.exit(-1) end 259 | -------------------------------------------------------------------------------- /doc/path.ldoc: -------------------------------------------------------------------------------- 1 | --- 2 | -- @module path 3 | -- 4 | -- @usage 5 | -- local PATH = require "path" 6 | -- 7 | -- -- suppose we run on windows 8 | -- assert(PATH.IS_WINDOWS) 9 | -- 10 | -- -- we can use system dependet function 11 | -- print(PATH.user_home()) -- C:\Documents and Settings\Admin 12 | -- print(PATH.currentdir()) -- C:\lua\5.1 13 | -- 14 | -- -- but we can use specific system path notation 15 | -- local ftp_path = PATH.new('/') 16 | -- print(ftp_path.join("/root", "some", "dir")) -- /root/some/dir 17 | -- 18 | -- -- All functions specific to system will fail 19 | -- assert(not pcall( function() ftp_path.currentdir() end) ) 20 | 21 | local path = {} 22 | 23 | --- The path separator. 24 | -- 25 | path.DIR_SEP = DIR_SEP 26 | 27 | --- ??? 28 | -- 29 | path.IS_WINDOWS = IS_WINDOWS 30 | 31 | -- 32 | -- PATH manipulation 33 | 34 | --- Unquote path 35 | -- @local 36 | function path.unquote(P)end 37 | 38 | --- Quote path 39 | -- @local 40 | function path.quote(P)end 41 | 42 | --- Returns true if path has trailing path name separator. 43 | -- 44 | function path.has_dir_end(P)end 45 | 46 | --- Removes the path name separator from the end of path, if it has it. 47 | -- 48 | function path.remove_dir_end(P)end 49 | 50 | --- Appends a path name separator to path if one does not exist. 51 | -- 52 | function path.ensure_dir_end(P)end 53 | 54 | --- Converts forward and backward slashes to @DIR_SEP 55 | -- @local 56 | function path.normalize_sep(P) end 57 | 58 | --- Normalize a path. 59 | -- This function normalize path name separators and collaps 60 | -- redundant separators and up-level references. 61 | -- E.g. `/a/fred/../b`, `/a//b`,`/a\\b`, `/a/./b` all normalize to `/a/b` 62 | -- 63 | function path.normalize(P) end 64 | 65 | --- Join one or more path components. 66 | -- If any component is an absolute path, all previous components 67 | -- are thrown away, and joining continues. 68 | -- If last parameter is empty string then result path will 69 | -- have trailing path name separator. 70 | -- 71 | function path.join(...)end 72 | 73 | --- Split the path into root and extension. 74 | -- If there no extension then returns empty string as extension. 75 | -- If basename starts with dot then 76 | -- @usage path.splitext("/some/file.txt") --> "/some/path", ".txt" 77 | -- @usage path.splitext("/some/.passwords") --> "/some/.passwords", "" 78 | -- @usage path.splitext("/some/.file.passwords") --> "/some/.file", ".passwords" 79 | function path.splitext(P) end 80 | 81 | --- Split the path into dirname and basename. 82 | -- 83 | function path.splitpath(P)end 84 | 85 | --- Alias to @{splitpath}. 86 | -- 87 | function path.split(P)end 88 | 89 | --- Split first path part for absolute path. 90 | -- 91 | function path.splitroot(P)end 92 | 93 | --- Split path into drive specification and path. 94 | -- 95 | -- On non Windows systems drive always empty string. 96 | function path.splitdrive(P)end 97 | 98 | --- Return the base name of path. 99 | -- 100 | function path.basename(P)end 101 | 102 | --- Return the directory name of path. 103 | -- 104 | function path.dirname(P)end 105 | 106 | --- Return the extension of path. 107 | -- 108 | function path.extension(P)end 109 | 110 | --- Return first path part for absolute path. 111 | -- On Windows this is drive letter (e.g. "c:\\some\path" => "c:"). 112 | -- On *nix this is first directory name (e.g. "/usr/etc" => "/usr"). 113 | -- 114 | function path.root(P)end 115 | 116 | --- Return `true` if path contain root part. 117 | -- 118 | function path.isfullpath(P)end 119 | 120 | --- Alias to @{isfullpath} 121 | -- 122 | function path.isabs(P)end 123 | 124 | -- 125 | -- FS manipulation 126 | 127 | --- return user_home dir 128 | -- 129 | function path.user_home()end 130 | 131 | --- Return file attributes. 132 | -- On Windows it is result of GetFileAttributesEx. 133 | -- 134 | --
depends on `path.fs` 135 | function path.flags(P)end 136 | 137 | --- Return path to temp directory. 138 | -- On Windows can use GetTempPath if Alien/FFI/afx have been found 139 | -- On Windows use TMP/TEMP environment variables 140 | -- 141 | --
depends on `path.fs` 142 | function path.tmpdir() end 143 | 144 | --- Return full path for temp file. 145 | -- 146 | --
depends on `path.fs` 147 | function path.tmpname()end 148 | 149 | --- Return size in bytes for file. 150 | -- 151 | --
depends on `path.fs` 152 | function path.size(P)end 153 | 154 | --- Alias to @{size} 155 | -- 156 | function path.getsize(P)end 157 | 158 | --- Return full normalized path. 159 | -- 160 | --
depends on `path.fs` 161 | function path.fullpath(P)end 162 | 163 | --- Alias to @{fullpath}. 164 | -- 165 | function path.abspath(P)end 166 | 167 | --- Return file attributes. 168 | -- 169 | -- @local 170 | --
depends on `path.fs` 171 | function path.attrib(P, ...)end 172 | 173 | --- Return path if path existing in file system. 174 | -- 175 | --
depends on `path.fs` 176 | function path.exists(P)end 177 | 178 | --- Return path if path refers to an existing directory. 179 | -- 180 | --
depends on `path.fs` 181 | function path.isdir(P)end 182 | 183 | --- Return path if path refers to an existing file. 184 | -- 185 | --
depends on `path.fs` 186 | function path.isfile(P)end 187 | 188 | --- Return path if path refers to an existing symbolic link. 189 | -- 190 | --
depends on `path.fs` 191 | function path.islink(P)end 192 | 193 | --- Return true if directory is empty. 194 | -- 195 | --
depends on `path.fs` 196 | function path.isempty(P)end 197 | 198 | --- Return creation file time. 199 | -- On *nix this is the time of the last metadata change. 200 | -- 201 | --
depends on `path.fs` 202 | function path.ctime(P)end 203 | 204 | --- Return last modification file time. 205 | -- 206 | --
depends on `path.fs` 207 | function path.mtime(P)end 208 | 209 | --- Return last access file time. 210 | -- 211 | --
depends on `path.fs` 212 | function path.atime(P)end 213 | 214 | --- Return `path.ctime` as `date` object. 215 | -- 216 | --
depends on `path.fs` and LuaDate 217 | function path.cdate(P)end 218 | 219 | --- Return `path.mtime` as `date` object. 220 | -- 221 | --
depends on `path.fs` and LuaDate 222 | function path.mdate(P)end 223 | 224 | --- Return `path.atime` as `date` object. 225 | -- 226 | --
depends on `path.fs` and LuaDate 227 | function path.adate(P)end 228 | 229 | --- Alias to @{ctime} 230 | -- 231 | function path.getctime(P)end 232 | 233 | --- Alias to @{mtime} 234 | -- 235 | function path.getmtime(P)end 236 | 237 | --- Alias to @{atime} 238 | -- 239 | function path.getatime(P)end 240 | 241 | --- Create new directory. 242 | -- This function also creates all nested directories if needed. 243 | -- 244 | --
depends on `path.fs` 245 | function path.mkdir(P)end 246 | 247 | --- Remove empty directory. 248 | -- 249 | --
depends on `path.fs` 250 | function path.rmdir(P)end 251 | 252 | --- Remove file or empty directory. 253 | -- 254 | -- @tparam string P 255 | -- @tparam ?remove_opt opt 256 | --
depends on `path.fs` 257 | function path.remove(P, opt)end 258 | 259 | --- Rename existed file. 260 | -- 261 | --
depends on `path.fs` 262 | function path.rename(from,to,force)end 263 | 264 | --- Copy file or directory tree. 265 | -- 266 | -- @tparam string from 267 | -- @tparam string to 268 | -- @tparam ?copy_opt|boolean opt 269 | --
depends on `path.fs` 270 | function path.copy(from,to,opt)end 271 | 272 | --- Return current work directory path. 273 | -- 274 | --
depends on `path.fs` 275 | function path.currentdir()end 276 | 277 | --- Change current work directory path. 278 | -- 279 | --
depends on `path.fs` 280 | function path.chdir(P)end 281 | 282 | --- Iterate over directory tree. 283 | -- 284 | --
depends on `path.fs` 285 | -- 286 | -- @usage -- clean currentdir 287 | -- path.each("./*", "f", path.remove, { 288 | -- delay = true; -- use snapshot of directory 289 | -- recurse = true; -- include subdirs 290 | -- reverse = true; -- subdirs at first 291 | -- }) 292 | -- 293 | -- @usage -- clean only files in currentdir 294 | -- path.each("./*", "f", path.remove, {skipdirs = true; delay = true}) 295 | -- 296 | -- @see path.fs.each 297 | -- @see path.fs.each_options 298 | -- 299 | function path.each(str_file, str_params, func_callback, tbl_option)end 300 | 301 | --- Iterate over directory tree. 302 | -- 303 | --
depends on `path.fs` 304 | -- 305 | -- @usage -- clean currentdir 306 | -- for fullpath in path.each("./*", "f", { 307 | -- delay = true; -- use snapshot of directory 308 | -- recurse = true; -- include subdirs 309 | -- reverse = true; -- subdirs at first 310 | -- })do path.remove(fullpath) end 311 | -- 312 | -- @see path.fs.each 313 | -- @see path.fs.each_options 314 | -- 315 | function path.each(str_file, str_params, tbl_option)end 316 | 317 | --- Iterate over directory tree. 318 | -- 319 | --
depends on `path.fs` 320 | -- 321 | -- @usage -- clean currentdir. for example we also retreave file mode 322 | -- path.each("./*", function(P, mode) 323 | -- if mode == 'directory' then 324 | -- path.rmdir(P) 325 | -- else 326 | -- path.remove(P) 327 | -- end 328 | -- end,{ 329 | -- param = "fm"; -- request full path and mode 330 | -- delay = true; -- use snapshot of directory 331 | -- recurse = true; -- include subdirs 332 | -- reverse = true; -- subdirs at first 333 | -- }) 334 | -- 335 | -- @see path.fs.each 336 | -- @see path.fs.each_options 337 | -- 338 | function path.each(str_file, func_callback, tbl_option)end 339 | 340 | --- Iterate over directory tree. 341 | -- This is common version of function. 342 | -- 343 | --
depends on `path.fs` 344 | -- 345 | -- @usage -- clean currentdir 346 | -- path.each("./*", { 347 | -- callback = path.remove, 348 | -- param = "f"; -- request full path 349 | -- delay = true; -- use snapshot of directory 350 | -- recurse = true; -- include subdirs 351 | -- reverse = true; -- subdirs at first 352 | -- }) 353 | -- 354 | -- @see path.fs.each 355 | -- @see path.fs.each_options 356 | -- 357 | function path.each(str_file, tbl_option)end 358 | 359 | --- Iterate over directory tree. 360 | -- 361 | --
depends on `path.fs` 362 | -- 363 | -- @see path.fs.each 364 | -- @see path.fs.each_options 365 | -- 366 | function path.each(func_callback, tbl_option)end 367 | 368 | --- Option table for `path.copy` function. 369 | -- 370 | -- @table copy_opt 371 | -- @tfield[opt=false] boolean delay 372 | -- @tfield[opt=false] boolean recurse 373 | -- @tfield[opt=false] boolean skipdirs 374 | -- @tfield[opt=false] boolean skipfiles 375 | -- @tfield[opt=function(src,dst) return true end] callable accept 376 | -- @tfield[opt=function(err, src, dst) return true end] callable error 377 | -- @see path.fs.each_options 378 | 379 | --- Option table for `path.remove` function. 380 | -- 381 | -- @table remove_opt 382 | -- @tfield[opt=true] boolean delay 383 | -- @tfield[opt=false] boolean recurse 384 | -- @tfield[opt=false] boolean skipdirs 385 | -- @tfield[opt=false] boolean skipfiles 386 | -- @tfield[opt=function(src,dst) return true end] callable accept 387 | -- @tfield[opt=function(err, src, dst) return true end] callable error 388 | -- @see path.fs.each_options 389 | -------------------------------------------------------------------------------- /lua/path/lfs/impl/fs.lua: -------------------------------------------------------------------------------- 1 | ------------------------------------------------------------------ 2 | -- 3 | -- Author: Alexey Melnichuk 4 | -- 5 | -- Copyright (C) 2013-2016 Alexey Melnichuk 6 | -- 7 | -- Licensed according to the included 'LICENCE' document 8 | -- 9 | -- This file is part of lua-path library. 10 | -- 11 | ------------------------------------------------------------------ 12 | 13 | return function(lfs) 14 | local os = require "os" 15 | 16 | local DIR_SEP = package.config:sub(1,1) 17 | local IS_WINDOWS = DIR_SEP == '\\' 18 | 19 | local _M = { 20 | DIR_SEP = DIR_SEP; 21 | } 22 | 23 | _M.currentdir = lfs.currentdir 24 | 25 | _M.attributes = lfs.attributes 26 | 27 | -- function _M.flags(P) end 28 | 29 | local attrib = lfs.attributes 30 | 31 | function _M.ctime(P) return attrib(P,'change') end 32 | 33 | function _M.atime(P) return attrib(P,'access') end 34 | 35 | function _M.mtime(P) return attrib(P,'modification') end 36 | 37 | function _M.size(P) return attrib(P,'size') end 38 | 39 | function _M.exists(P) return attrib(P,'mode') ~= nil and P end 40 | 41 | function _M.isdir(P) return attrib(P,'mode') == 'directory' and P end 42 | 43 | function _M.isfile(P) return attrib(P,'mode') == 'file' and P end 44 | 45 | function _M.islink(P) return attrib(P,'mode') == 'link' and P end 46 | 47 | _M.mkdir = lfs.mkdir 48 | 49 | _M.rmdir = lfs.rmdir 50 | 51 | _M.chdir = lfs.chdir 52 | 53 | _M.link = lfs.link 54 | 55 | _M.setmode = lfs.setmode 56 | 57 | function _M.copy(src, dst, force) 58 | if not IS_WINDOWS then 59 | if _M.isdir(src) or _M.isdir(dst) then 60 | return nil, 'can not copy directories' 61 | end 62 | end 63 | local f, err = io.open(src, 'rb') 64 | if not f then return nil, err end 65 | 66 | if not force then 67 | local t, err = io.open(dst, 'rb' ) 68 | if t then 69 | f:close() 70 | t:close() 71 | return nil, "file alredy exists" 72 | end 73 | end 74 | 75 | local t, err = io.open(dst, 'w+b') 76 | if not t then 77 | f:close() 78 | return nil, err 79 | end 80 | 81 | local CHUNK_SIZE = 4096 82 | while true do 83 | local chunk = f:read(CHUNK_SIZE) 84 | if not chunk then break end 85 | local ok, err = t:write(chunk) 86 | if not ok then 87 | t:close() 88 | f:close() 89 | return nil, err or "can not write" 90 | end 91 | end 92 | 93 | t:close() 94 | f:close() 95 | return true 96 | end 97 | 98 | function _M.move(src, dst, flags) 99 | if flags and _M.exists(dst) and _M.exists(src) then 100 | local ok, err = _M.remove(dst) 101 | -- do we have to remove dir? 102 | -- if not ok then ok, err = _M.rmdir(dst) end 103 | if not ok then return nil, err end 104 | end 105 | if (not IS_WINDOWS) and _M.exists(dst) then 106 | -- on windows os.rename return error when dst exists, 107 | -- but on linux its just replace existed file 108 | return nil, "destination alredy exists" 109 | end 110 | return os.rename(src, dst) 111 | end 112 | 113 | function _M.remove(P) 114 | -- on windows os.remove can not remove dir 115 | if (not IS_WINDOWS) and _M.isdir(P) then 116 | return nil, "remove method can not remove dirs" 117 | end 118 | return os.remove(P) 119 | end 120 | 121 | local function splitpath(P) return string.match(P,"^(.-)[\\/]?([^\\/]*)$") end 122 | 123 | function _M.tmpdir() 124 | if IS_WINDOWS then 125 | for _, p in ipairs{'TEMP', 'TMP'} do 126 | local dir = os.getenv(p) 127 | if dir and dir ~= '' then 128 | return dir 129 | end 130 | end 131 | end 132 | return (splitpath(os.tmpname())) 133 | end 134 | 135 | _M.dir = lfs.dir 136 | 137 | _M.touch = lfs.touch 138 | 139 | local function isdots(P) 140 | return P == '.' or P == '..' 141 | end 142 | 143 | local foreach_impl 144 | 145 | local function do_foreach_recurse(base, match, callback, option) 146 | local dir_next, dir = lfs.dir(base) 147 | for name in dir_next, dir do if not isdots(name) then 148 | local path = base .. DIR_SEP .. name 149 | if _M.attributes(path,"mode") == "directory" then 150 | local ret, err = foreach_impl(path, match, callback, option) 151 | if ret or err then 152 | if dir then dir:close() end 153 | return ret, err 154 | end 155 | end 156 | end end 157 | end 158 | 159 | foreach_impl = function(base, match, callback, option) 160 | local tmp, origin_cb 161 | if option.delay then 162 | tmp, origin_cb, callback = {}, callback, function(base,name,fd) 163 | table.insert(tmp, {base,name,fd}) 164 | end; 165 | end 166 | 167 | if option.recurse and option.reverse == true then 168 | local ok, err = do_foreach_recurse(base, match, callback, option) 169 | if ok or err then return ok, err end 170 | end 171 | 172 | local dir_next, dir = lfs.dir(base) 173 | for name in dir_next, dir do if option.skipdots == false or not isdots(name) then 174 | local path = base .. DIR_SEP .. name 175 | local attr = _M.attributes(path) 176 | if not attr then return end 177 | 178 | if (option.skipdirs and attr.mode == "directory") 179 | or (option.skipfiles and attr.mode == "file") 180 | then else 181 | if match(name) then 182 | local ret, err = callback(base, name, attr) 183 | if ret or err then 184 | if dir then dir:close() end 185 | return ret, err 186 | end 187 | end 188 | end 189 | 190 | local can_recurse = (not option.delay) and option.recurse and (option.reverse == nil) 191 | if can_recurse and attr.mode == "directory" and not isdots(name) then 192 | local ret, err = foreach_impl(path, match, callback, option) 193 | if ret or err then 194 | if dir then dir:close() end 195 | return ret, err 196 | end 197 | end 198 | end end 199 | 200 | if option.delay then 201 | for _, t in ipairs(tmp) do 202 | local ok, err = origin_cb(t[1], t[2], t[3]) 203 | if ok or err then return ok, err end 204 | end 205 | end 206 | 207 | if option.recurse and (not option.reverse) then 208 | if option.delay or (option.reverse == false) then 209 | return do_foreach_recurse(base, match, origin_cb or callback, option) 210 | end 211 | end 212 | end 213 | 214 | local function filePat2rexPat(pat) 215 | if pat:find("[*?]") then 216 | local post = '$' 217 | if pat:find("*", 1, true) then 218 | if pat:find(".", 1, true) then post = '[^.]*$' 219 | else post = '' end 220 | end 221 | pat = "^" .. pat:gsub("%.","%%."):gsub("%*",".*"):gsub("%?", ".?") .. post 222 | else 223 | pat = "^" .. pat:gsub("%.","%%.") .. "$" 224 | end 225 | if IS_WINDOWS then pat = pat:upper() end 226 | return pat 227 | end 228 | 229 | local function match_pat(pat) 230 | pat = filePat2rexPat(pat) 231 | return IS_WINDOWS 232 | and function(s) return nil ~= string.find(string.upper(s), pat) end 233 | or function(s) return nil ~= string.find(s, pat) end 234 | end 235 | 236 | function _M.foreach(base, callback, option) 237 | local base, mask = splitpath(base, DIR_SEP) 238 | if mask ~= '' then mask = match_pat(mask) 239 | else mask = function() return true end end 240 | return foreach_impl(base, mask, function(base, name, fd) 241 | return callback(base .. DIR_SEP .. name, fd) 242 | end, option or {}) 243 | end 244 | 245 | local attribs = { 246 | f = function(base, name, fd) return base..DIR_SEP..name end; 247 | p = function(base, name, fd) return base end; 248 | n = function(base, name, fd) return name end; 249 | m = function(base, name, fd) return fd.mode end; 250 | a = function(base, name, fd) return fd end; 251 | z = function(base, name, fd) return fd.size end; 252 | t = function(base, name, fd) return fd.modification end; 253 | c = function(base, name, fd) return fd.change end; 254 | l = function(base, name, fd) return fd.access end; 255 | } 256 | 257 | local function make_attrib(str) 258 | local t = {} 259 | for i = 1, #str do 260 | local ch = str:sub(i,i) 261 | local fn = attribs[ ch ] 262 | if not fn then return nil, 'unknown file attribute: ' .. ch end 263 | table.insert(t, fn) 264 | end 265 | 266 | return function(...) 267 | local res = {n = #t} 268 | for i, f in ipairs(t) do 269 | local ok, err = f(...) 270 | if ok == nil then return nil, err end 271 | table.insert(res, ok) 272 | end 273 | return res 274 | end 275 | end 276 | 277 | function _M.each_impl(option) 278 | if not option.file then return nil, 'no file mask present' end 279 | local base, mask = splitpath( option.file, DIR_SEP ) 280 | if mask ~= '' then mask = match_pat(mask) 281 | else mask = function() return true end end 282 | 283 | local get_params, err = make_attrib(option.param or 'f') 284 | if not get_params then return nil, err end 285 | local unpack = unpack or table.unpack 286 | 287 | local filter = option.filter 288 | 289 | if option.callback then 290 | local callback = option.callback 291 | 292 | local function cb(base, name, fd) 293 | local params = assert(get_params(base, name, fd)) 294 | if filter and (not filter(unpack(params, 1, params.n))) then return end 295 | return callback(unpack(params, 1, params.n)) 296 | end 297 | 298 | return foreach_impl(base, mask, cb, option) 299 | else 300 | local function cb(base, name, fd) 301 | local params = assert(get_params(base, name, fd)) 302 | if filter and (not filter(unpack(params, 1, params.n))) then return end 303 | coroutine.yield(params) 304 | end 305 | local co = coroutine.create(function() 306 | foreach_impl(base, mask, cb, option) 307 | end) 308 | return function() 309 | local status, params = coroutine.resume(co) 310 | if status then if params then return unpack(params, 1, params.n) end 311 | else error(params, 2) end 312 | end 313 | end 314 | end 315 | 316 | local create_each = require "path.findfile".load 317 | 318 | _M.each = create_each(_M.each_impl) 319 | 320 | local function match_pat_selftest() 321 | 322 | local t = { 323 | ["*.txt"] = { 324 | [".txt" ] = true; 325 | ["1.txt" ] = true; 326 | ["1.txtdat" ] = true; 327 | [".txtdat" ] = false; 328 | [".txt.dat" ] = false; 329 | [".dat.txt" ] = true; 330 | }; 331 | ["*.txt*"] = { 332 | [".txt" ] = true; 333 | ["1.txt" ] = true; 334 | ["1.txtdat" ] = true; 335 | [".txtdat" ] = true; 336 | [".txt.dat" ] = true; 337 | [".dat.txt" ] = true; 338 | }; 339 | ["?.txt"] = { 340 | [".txt" ] = true; 341 | ["1.txt" ] = true; 342 | ["1.txtdat" ] = false; 343 | [".txtdat" ] = false; 344 | [".txt.dat" ] = false; 345 | [".dat.txt" ] = false; 346 | }; 347 | ["1?.txt"] = { 348 | [".txt" ] = false; 349 | ["1.txt" ] = true; 350 | ["1.txtdat" ] = false; 351 | [".txtdat" ] = false; 352 | [".txt.dat" ] = false; 353 | [".dat.txt" ] = false; 354 | }; 355 | ["1*.txt"] = { 356 | [".txt" ] = false; 357 | ["1.txt" ] = true; 358 | ["1.txtdat" ] = true; 359 | [".txtdat" ] = false; 360 | [".txt.dat" ] = false; 361 | [".dat.txt" ] = false; 362 | }; 363 | } 364 | 365 | local function test_match(pat, t) 366 | local cmp = match_pat(pat) 367 | for fname, status in pairs(t) do 368 | if status ~= cmp(fname) then 369 | io.write("Pat: ", pat, " Name: ", fname, " Expected: ", tostring(status), " Got: ", tostring(cmp(fname)), "\n") 370 | end 371 | end 372 | end 373 | 374 | for k, v in pairs(t) do 375 | test_match(k,v) 376 | end 377 | 378 | end 379 | 380 | return _M 381 | end -------------------------------------------------------------------------------- /cmake/lua.cmake: -------------------------------------------------------------------------------- 1 | # LuaDist CMake utility library for Lua. 2 | # 3 | # Copyright (C) 2007-2012 LuaDist. 4 | # by David Manura, Peter Drahos 5 | # Redistribution and use of this file is allowed according to the terms of the MIT license. 6 | # For details see the COPYRIGHT file distributed with LuaDist. 7 | # Please note that the package source code is licensed under its own license. 8 | 9 | set ( INSTALL_LMOD ${INSTALL_LIB}/lua 10 | CACHE PATH "Directory to install Lua modules." ) 11 | set ( INSTALL_CMOD ${INSTALL_LIB}/lua 12 | CACHE PATH "Directory to install Lua binary modules." ) 13 | 14 | option ( LUA_SKIP_WRAPPER 15 | "Do not build and install Lua executable wrappers." OFF ) 16 | option ( LUA_STATIC_MODULE "Build modules for static linking" OFF ) 17 | 18 | # List of (Lua module name, file path) pairs. 19 | # Used internally by add_lua_test. Built by add_lua_module. 20 | set ( _lua_modules ) 21 | 22 | # utility function: appends path `path` to path `basepath`, properly 23 | # handling cases when `path` may be relative or absolute. 24 | macro ( _append_path basepath path result ) 25 | if ( IS_ABSOLUTE "${path}" ) 26 | set ( ${result} "${path}" ) 27 | else () 28 | set ( ${result} "${basepath}/${path}" ) 29 | endif () 30 | endmacro () 31 | 32 | # install_lua_executable ( target source ) 33 | # Automatically generate a binary if srlua package is available 34 | # The application or its source will be placed into /bin 35 | # If the application source did not have .lua suffix then it will be added 36 | # USE: lua_executable ( sputnik src/sputnik.lua ) 37 | macro ( install_lua_executable _name _source ) 38 | get_filename_component ( _source_name ${_source} NAME_WE ) 39 | # Find srlua and glue 40 | find_program( SRLUA_EXECUTABLE NAMES srlua ) 41 | find_program( GLUE_EXECUTABLE NAMES glue ) 42 | # Executable output 43 | set ( _exe ${CMAKE_CURRENT_BINARY_DIR}/${_name}${CMAKE_EXECUTABLE_SUFFIX} ) 44 | if ( NOT SKIP_LUA_WRAPPER AND SRLUA_EXECUTABLE AND GLUE_EXECUTABLE ) 45 | # Generate binary gluing the lua code to srlua, this is a robuust approach for most systems 46 | add_custom_command( 47 | OUTPUT ${_exe} 48 | COMMAND ${GLUE_EXECUTABLE} 49 | ARGS ${SRLUA_EXECUTABLE} ${_source} ${_exe} 50 | DEPENDS ${_source} 51 | WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} 52 | VERBATIM 53 | ) 54 | # Make sure we have a target associated with the binary 55 | add_custom_target(${_name} ALL 56 | DEPENDS ${_exe} 57 | ) 58 | # Install with run permissions 59 | install ( PROGRAMS ${_exe} DESTINATION ${INSTALL_BIN} COMPONENT Runtime) 60 | # Also install source as optional resurce 61 | install ( FILES ${_source} DESTINATION ${INSTALL_FOO} COMPONENT Other ) 62 | else() 63 | # Install into bin as is but without the lua suffix, we assume the executable uses UNIX shebang/hash-bang magic 64 | install ( PROGRAMS ${_source} DESTINATION ${INSTALL_BIN} 65 | RENAME ${_source_name} 66 | COMPONENT Runtime 67 | ) 68 | endif() 69 | endmacro () 70 | 71 | macro ( _lua_module_helper is_install _name ) 72 | parse_arguments ( _MODULE "LINK;ALL_IN_ONE" "" ${ARGN} ) 73 | # _target is CMake-compatible target name for module (e.g. socket_core). 74 | # _module is relative path of target (e.g. socket/core), 75 | # without extension (e.g. .lua/.so/.dll). 76 | # _MODULE_SRC is list of module source files (e.g. .lua and .c files). 77 | # _MODULE_NAMES is list of module names (e.g. socket.core). 78 | if ( _MODULE_ALL_IN_ONE ) 79 | string ( REGEX REPLACE "\\..*" "" _target "${_name}" ) 80 | string ( REGEX REPLACE "\\..*" "" _module "${_name}" ) 81 | set ( _target "${_target}_all_in_one") 82 | set ( _MODULE_SRC ${_MODULE_ALL_IN_ONE} ) 83 | set ( _MODULE_NAMES ${_name} ${_MODULE_DEFAULT_ARGS} ) 84 | else () 85 | string ( REPLACE "." "_" _target "${_name}" ) 86 | string ( REPLACE "." "/" _module "${_name}" ) 87 | set ( _MODULE_SRC ${_MODULE_DEFAULT_ARGS} ) 88 | set ( _MODULE_NAMES ${_name} ) 89 | endif () 90 | if ( NOT _MODULE_SRC ) 91 | message ( FATAL_ERROR "no module sources specified" ) 92 | endif () 93 | list ( GET _MODULE_SRC 0 _first_source ) 94 | 95 | get_filename_component ( _ext ${_first_source} EXT ) 96 | if ( _ext STREQUAL ".lua" ) # Lua source module 97 | list ( LENGTH _MODULE_SRC _len ) 98 | if ( _len GREATER 1 ) 99 | message ( FATAL_ERROR "more than one source file specified" ) 100 | endif () 101 | 102 | set ( _module "${_module}.lua" ) 103 | 104 | get_filename_component ( _module_dir ${_module} PATH ) 105 | get_filename_component ( _module_filename ${_module} NAME ) 106 | _append_path ( "${CMAKE_CURRENT_SOURCE_DIR}" "${_first_source}" _module_path ) 107 | list ( APPEND _lua_modules "${_name}" "${_module_path}" ) 108 | 109 | if ( ${is_install} ) 110 | install ( FILES ${_first_source} DESTINATION ${INSTALL_LMOD}/${_module_dir} 111 | RENAME ${_module_filename} 112 | COMPONENT Runtime 113 | ) 114 | endif () 115 | else () # Lua C binary module 116 | enable_language ( C ) 117 | find_package ( Lua REQUIRED ) 118 | include_directories ( ${LUA_INCLUDE_DIR} ) 119 | 120 | set ( _module "${_module}${CMAKE_SHARED_MODULE_SUFFIX}" ) 121 | 122 | get_filename_component ( _module_dir ${_module} PATH ) 123 | get_filename_component ( _module_filenamebase ${_module} NAME_WE ) 124 | foreach ( _thisname ${_MODULE_NAMES} ) 125 | list ( APPEND _lua_modules "${_thisname}" 126 | "${CMAKE_CURRENT_BINARY_DIR}/\${CMAKE_CFG_INTDIR}/${_module}" ) 127 | endforeach () 128 | 129 | # Static module (not linking to lua) 130 | if ( LUA_STATIC_MODULE ) 131 | add_library( ${_target} STATIC ${_MODULE_SRC}) 132 | target_link_libraries ( ${_target} ${_MODULE_LINK} ) 133 | else () 134 | # Dynamic module 135 | add_library( ${_target} MODULE ${_MODULE_SRC}) 136 | target_link_libraries ( ${_target} ${LUA_LIBRARY} ${_MODULE_LINK} ) 137 | endif () 138 | 139 | set_target_properties ( ${_target} PROPERTIES 140 | ARCHIVE_OUTPUT_DIRECTORY "${_module_dir}" 141 | LIBRARY_OUTPUT_DIRECTORY "${_module_dir}" 142 | PREFIX "" 143 | OUTPUT_NAME "${_module_filenamebase}" ) 144 | if ( ${is_install} ) 145 | install ( TARGETS ${_target} 146 | LIBRARY DESTINATION ${INSTALL_CMOD}/${_module_dir} 147 | COMPONENT Runtime 148 | ARCHIVE DESTINATION ${INSTALL_CMOD}/${_module_dir} 149 | COMPONENT Library ) 150 | endif () 151 | endif () 152 | endmacro () 153 | 154 | # add_lua_module 155 | # Builds a Lua source module into a destination locatable by Lua 156 | # require syntax. 157 | # Binary modules are also supported where this function takes sources and 158 | # libraries to compile separated by LINK keyword. 159 | # USE: add_lua_module ( socket.http src/http.lua ) 160 | # USE2: add_lua_module ( mime.core src/mime.c ) 161 | # USE3: add_lua_module ( socket.core ${SRC_SOCKET} LINK ${LIB_SOCKET} ) 162 | # USE4: add_lua_module ( ssl.context ssl.core ALL_IN_ONE src/context.c src/ssl.c ) 163 | # This form builds an "all-in-one" module (e.g. ssl.so or ssl.dll containing 164 | # both modules ssl.context and ssl.core). The CMake target name will be 165 | # ssl_all_in_one. 166 | # Also sets variable _module_path (relative path where module typically 167 | # would be installed). 168 | macro ( add_lua_module ) 169 | _lua_module_helper ( 0 ${ARGN} ) 170 | endmacro () 171 | 172 | 173 | # install_lua_module 174 | # This is the same as `add_lua_module` but also installs the module. 175 | # USE: install_lua_module ( socket.http src/http.lua ) 176 | # USE2: install_lua_module ( mime.core src/mime.c ) 177 | # USE3: install_lua_module ( socket.core ${SRC_SOCKET} LINK ${LIB_SOCKET} ) 178 | macro ( install_lua_module ) 179 | _lua_module_helper ( 1 ${ARGN} ) 180 | endmacro () 181 | 182 | # Builds string representing Lua table mapping Lua modules names to file 183 | # paths. Used internally. 184 | macro ( _make_module_table _outvar ) 185 | set ( ${_outvar} ) 186 | list ( LENGTH _lua_modules _n ) 187 | if ( ${_n} GREATER 0 ) # avoids cmake complaint 188 | foreach ( _i RANGE 1 ${_n} 2 ) 189 | list ( GET _lua_modules ${_i} _path ) 190 | math ( EXPR _ii ${_i}-1 ) 191 | list ( GET _lua_modules ${_ii} _name ) 192 | set ( ${_outvar} "${_table} ['${_name}'] = '${_path}'\;\n") 193 | endforeach () 194 | endif () 195 | set ( ${_outvar} 196 | "local modules = { 197 | ${_table}}" ) 198 | endmacro () 199 | 200 | # add_lua_test ( _testfile [ WORKING_DIRECTORY _working_dir ] ) 201 | # Runs Lua script `_testfile` under CTest tester. 202 | # Optional named argument `WORKING_DIRECTORY` is current working directory to 203 | # run test under (defaults to ${CMAKE_CURRENT_BINARY_DIR}). 204 | # Both paths, if relative, are relative to ${CMAKE_CURRENT_SOURCE_DIR}. 205 | # Any modules previously defined with install_lua_module are automatically 206 | # preloaded (via package.preload) prior to running the test script. 207 | # Under LuaDist, set test=true in config.lua to enable testing. 208 | # USE: add_lua_test ( test/test1.lua [args...] [WORKING_DIRECTORY dir]) 209 | macro ( add_lua_test _testfile ) 210 | if ( NOT SKIP_TESTING ) 211 | parse_arguments ( _ARG "WORKING_DIRECTORY" "" ${ARGN} ) 212 | include ( CTest ) 213 | find_program ( LUA NAMES lua lua.bat ) 214 | get_filename_component ( TESTFILEABS ${_testfile} ABSOLUTE ) 215 | get_filename_component ( TESTFILENAME ${_testfile} NAME ) 216 | get_filename_component ( TESTFILEBASE ${_testfile} NAME_WE ) 217 | 218 | # Write wrapper script. 219 | # Note: One simple way to allow the script to find modules is 220 | # to just put them in package.preload. 221 | set ( TESTWRAPPER ${CMAKE_CURRENT_BINARY_DIR}/${TESTFILENAME} ) 222 | _make_module_table ( _table ) 223 | set ( TESTWRAPPERSOURCE 224 | "local CMAKE_CFG_INTDIR = ... or '.' 225 | ${_table} 226 | local function preload_modules(modules) 227 | for name, path in pairs(modules) do 228 | if path:match'%.lua' then 229 | package.preload[name] = assert(loadfile(path)) 230 | else 231 | local name = name:gsub('.*%-', '') -- remove any hyphen prefix 232 | local symbol = 'luaopen_' .. name:gsub('%.', '_') 233 | --improve: generalize to support all-in-one loader? 234 | local path = path:gsub('%$%{CMAKE_CFG_INTDIR%}', CMAKE_CFG_INTDIR) 235 | package.preload[name] = assert(package.loadlib(path, symbol)) 236 | end 237 | end 238 | end 239 | preload_modules(modules) 240 | arg[0] = '${TESTFILEABS}' 241 | table.remove(arg, 1) 242 | return assert(loadfile '${TESTFILEABS}')(unpack(arg)) 243 | " ) 244 | if ( _ARG_WORKING_DIRECTORY ) 245 | get_filename_component ( 246 | TESTCURRENTDIRABS ${_ARG_WORKING_DIRECTORY} ABSOLUTE ) 247 | # note: CMake 2.6 (unlike 2.8) lacks WORKING_DIRECTORY parameter. 248 | set ( _pre ${CMAKE_COMMAND} -E chdir "${TESTCURRENTDIRABS}" ) 249 | endif () 250 | file ( WRITE ${TESTWRAPPER} ${TESTWRAPPERSOURCE}) 251 | add_test ( NAME ${TESTFILEBASE} COMMAND ${_pre} ${LUA} 252 | ${TESTWRAPPER} "${CMAKE_CFG_INTDIR}" 253 | ${_ARG_DEFAULT_ARGS} ) 254 | endif () 255 | # see also http://gdcm.svn.sourceforge.net/viewvc/gdcm/Sandbox/CMakeModules/UsePythonTest.cmake 256 | # Note: ${CMAKE_CFG_INTDIR} is a command-line argument to allow proper 257 | # expansion by the native build tool. 258 | endmacro () 259 | 260 | 261 | # Converts Lua source file `_source` to binary string embedded in C source 262 | # file `_target`. Optionally compiles Lua source to byte code (not available 263 | # under LuaJIT2, which doesn't have a bytecode loader). Additionally, Lua 264 | # versions of bin2c [1] and luac [2] may be passed respectively as additional 265 | # arguments. 266 | # 267 | # [1] http://lua-users.org/wiki/BinToCee 268 | # [2] http://lua-users.org/wiki/LuaCompilerInLua 269 | function ( add_lua_bin2c _target _source ) 270 | find_program ( LUA NAMES lua lua.bat ) 271 | execute_process ( COMMAND ${LUA} -e "string.dump(function()end)" 272 | RESULT_VARIABLE _LUA_DUMP_RESULT ERROR_QUIET ) 273 | if ( NOT ${_LUA_DUMP_RESULT} ) 274 | SET ( HAVE_LUA_DUMP true ) 275 | endif () 276 | message ( "-- string.dump=${HAVE_LUA_DUMP}" ) 277 | 278 | if ( ARGV2 ) 279 | get_filename_component ( BIN2C ${ARGV2} ABSOLUTE ) 280 | set ( BIN2C ${LUA} ${BIN2C} ) 281 | else () 282 | find_program ( BIN2C NAMES bin2c bin2c.bat ) 283 | endif () 284 | if ( HAVE_LUA_DUMP ) 285 | if ( ARGV3 ) 286 | get_filename_component ( LUAC ${ARGV3} ABSOLUTE ) 287 | set ( LUAC ${LUA} ${LUAC} ) 288 | else () 289 | find_program ( LUAC NAMES luac luac.bat ) 290 | endif () 291 | endif ( HAVE_LUA_DUMP ) 292 | message ( "-- bin2c=${BIN2C}" ) 293 | message ( "-- luac=${LUAC}" ) 294 | 295 | get_filename_component ( SOURCEABS ${_source} ABSOLUTE ) 296 | if ( HAVE_LUA_DUMP ) 297 | get_filename_component ( SOURCEBASE ${_source} NAME_WE ) 298 | add_custom_command ( 299 | OUTPUT ${_target} DEPENDS ${_source} 300 | COMMAND ${LUAC} -o ${CMAKE_CURRENT_BINARY_DIR}/${SOURCEBASE}.lo 301 | ${SOURCEABS} 302 | COMMAND ${BIN2C} ${CMAKE_CURRENT_BINARY_DIR}/${SOURCEBASE}.lo 303 | ">${_target}" ) 304 | else () 305 | add_custom_command ( 306 | OUTPUT ${_target} DEPENDS ${SOURCEABS} 307 | COMMAND ${BIN2C} ${_source} ">${_target}" ) 308 | endif () 309 | endfunction() 310 | -------------------------------------------------------------------------------- /cmake/dist.cmake: -------------------------------------------------------------------------------- 1 | # LuaDist CMake utility library. 2 | # Provides sane project defaults and macros common to LuaDist CMake builds. 3 | # 4 | # Copyright (C) 2007-2012 LuaDist. 5 | # by David Manura, Peter Drahoš 6 | # Redistribution and use of this file is allowed according to the terms of the MIT license. 7 | # For details see the COPYRIGHT file distributed with LuaDist. 8 | # Please note that the package source code is licensed under its own license. 9 | 10 | ## Extract information from dist.info 11 | if ( NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/dist.info ) 12 | message ( FATAL_ERROR 13 | "Missing dist.info file (${CMAKE_CURRENT_SOURCE_DIR}/dist.info)." ) 14 | endif () 15 | file ( READ ${CMAKE_CURRENT_SOURCE_DIR}/dist.info DIST_INFO ) 16 | if ( "${DIST_INFO}" STREQUAL "" ) 17 | message ( FATAL_ERROR "Failed to load dist.info." ) 18 | endif () 19 | # Reads field `name` from dist.info string `DIST_INFO` into variable `var`. 20 | macro ( _parse_dist_field name var ) 21 | string ( REGEX REPLACE ".*${name}[ \t]?=[ \t]?[\"']([^\"']+)[\"'].*" "\\1" 22 | ${var} "${DIST_INFO}" ) 23 | if ( ${var} STREQUAL DIST_INFO ) 24 | message ( FATAL_ERROR "Failed to extract \"${var}\" from dist.info" ) 25 | endif () 26 | endmacro () 27 | # 28 | _parse_dist_field ( name DIST_NAME ) 29 | _parse_dist_field ( version DIST_VERSION ) 30 | _parse_dist_field ( license DIST_LICENSE ) 31 | _parse_dist_field ( author DIST_AUTHOR ) 32 | _parse_dist_field ( maintainer DIST_MAINTAINER ) 33 | _parse_dist_field ( url DIST_URL ) 34 | _parse_dist_field ( desc DIST_DESC ) 35 | message ( "DIST_NAME: ${DIST_NAME}") 36 | message ( "DIST_VERSION: ${DIST_VERSION}") 37 | message ( "DIST_LICENSE: ${DIST_LICENSE}") 38 | message ( "DIST_AUTHOR: ${DIST_AUTHOR}") 39 | message ( "DIST_MAINTAINER: ${DIST_MAINTAINER}") 40 | message ( "DIST_URL: ${DIST_URL}") 41 | message ( "DIST_DESC: ${DIST_DESC}") 42 | string ( REGEX REPLACE ".*depends[ \t]?=[ \t]?[\"']([^\"']+)[\"'].*" "\\1" 43 | DIST_DEPENDS ${DIST_INFO} ) 44 | if ( DIST_DEPENDS STREQUAL DIST_INFO ) 45 | set ( DIST_DEPENDS "" ) 46 | endif () 47 | message ( "DIST_DEPENDS: ${DIST_DEPENDS}") 48 | ## 2DO: Parse DIST_DEPENDS and try to install Dependencies with automatically using externalproject_add 49 | 50 | 51 | ## INSTALL DEFAULTS (Relative to CMAKE_INSTALL_PREFIX) 52 | # Primary paths 53 | set ( INSTALL_BIN bin CACHE PATH "Where to install binaries to." ) 54 | set ( INSTALL_LIB lib CACHE PATH "Where to install libraries to." ) 55 | set ( INSTALL_INC include CACHE PATH "Where to install headers to." ) 56 | set ( INSTALL_ETC etc CACHE PATH "Where to store configuration files" ) 57 | set ( INSTALL_SHARE share CACHE PATH "Directory for shared data." ) 58 | 59 | # Secondary paths 60 | option ( INSTALL_VERSION 61 | "Install runtime libraries and executables with version information." OFF) 62 | set ( INSTALL_DATA ${INSTALL_SHARE}/${DIST_NAME} CACHE PATH 63 | "Directory the package can store documentation, tests or other data in.") 64 | set ( INSTALL_DOC ${INSTALL_DATA}/doc CACHE PATH 65 | "Recommended directory to install documentation into.") 66 | set ( INSTALL_EXAMPLE ${INSTALL_DATA}/example CACHE PATH 67 | "Recommended directory to install examples into.") 68 | set ( INSTALL_TEST ${INSTALL_DATA}/test CACHE PATH 69 | "Recommended directory to install tests into.") 70 | set ( INSTALL_FOO ${INSTALL_DATA}/etc CACHE PATH 71 | "Where to install additional files") 72 | 73 | # Tweaks and other defaults 74 | # Setting CMAKE to use loose block and search for find modules in source directory 75 | set ( CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS true ) 76 | set ( CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH} ) 77 | option ( BUILD_SHARED_LIBS "Build shared libraries" ON ) 78 | 79 | # In MSVC, prevent warnings that can occur when using standard libraries. 80 | if ( MSVC ) 81 | add_definitions ( -D_CRT_SECURE_NO_WARNINGS ) 82 | endif () 83 | 84 | # RPath and relative linking 85 | option ( USE_RPATH "Use relative linking." ON) 86 | if ( USE_RPATH ) 87 | string ( REGEX REPLACE "[^!/]+" ".." UP_DIR ${INSTALL_BIN} ) 88 | set ( CMAKE_SKIP_BUILD_RPATH FALSE CACHE STRING "" FORCE ) 89 | set ( CMAKE_BUILD_WITH_INSTALL_RPATH FALSE CACHE STRING "" FORCE ) 90 | set ( CMAKE_INSTALL_RPATH $ORIGIN/${UP_DIR}/${INSTALL_LIB} 91 | CACHE STRING "" FORCE ) 92 | set ( CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE CACHE STRING "" FORCE ) 93 | set ( CMAKE_INSTALL_NAME_DIR @executable_path/${UP_DIR}/${INSTALL_LIB} 94 | CACHE STRING "" FORCE ) 95 | endif () 96 | 97 | ## MACROS 98 | # Parser macro 99 | macro ( parse_arguments prefix arg_names option_names) 100 | set ( DEFAULT_ARGS ) 101 | foreach ( arg_name ${arg_names} ) 102 | set ( ${prefix}_${arg_name} ) 103 | endforeach () 104 | foreach ( option ${option_names} ) 105 | set ( ${prefix}_${option} FALSE ) 106 | endforeach () 107 | 108 | set ( current_arg_name DEFAULT_ARGS ) 109 | set ( current_arg_list ) 110 | foreach ( arg ${ARGN} ) 111 | set ( larg_names ${arg_names} ) 112 | list ( FIND larg_names "${arg}" is_arg_name ) 113 | if ( is_arg_name GREATER -1 ) 114 | set ( ${prefix}_${current_arg_name} ${current_arg_list} ) 115 | set ( current_arg_name ${arg} ) 116 | set ( current_arg_list ) 117 | else () 118 | set ( loption_names ${option_names} ) 119 | list ( FIND loption_names "${arg}" is_option ) 120 | if ( is_option GREATER -1 ) 121 | set ( ${prefix}_${arg} TRUE ) 122 | else () 123 | set ( current_arg_list ${current_arg_list} ${arg} ) 124 | endif () 125 | endif () 126 | endforeach () 127 | set ( ${prefix}_${current_arg_name} ${current_arg_list} ) 128 | endmacro () 129 | 130 | 131 | # install_executable ( executable_targets ) 132 | # Installs any executables generated using "add_executable". 133 | # USE: install_executable ( lua ) 134 | # NOTE: subdirectories are NOT supported 135 | set ( CPACK_COMPONENT_RUNTIME_DISPLAY_NAME "${DIST_NAME} Runtime" ) 136 | set ( CPACK_COMPONENT_RUNTIME_DESCRIPTION 137 | "Executables and runtime libraries. Installed into ${INSTALL_BIN}." ) 138 | macro ( install_executable ) 139 | foreach ( _file ${ARGN} ) 140 | if ( INSTALL_VERSION ) 141 | set_target_properties ( ${_file} PROPERTIES VERSION ${DIST_VERSION} 142 | SOVERSION ${DIST_VERSION} ) 143 | endif () 144 | install ( TARGETS ${_file} RUNTIME DESTINATION ${INSTALL_BIN} 145 | COMPONENT Runtime ) 146 | endforeach() 147 | endmacro () 148 | 149 | # install_library ( library_targets ) 150 | # Installs any libraries generated using "add_library" into apropriate places. 151 | # USE: install_library ( libexpat ) 152 | # NOTE: subdirectories are NOT supported 153 | set ( CPACK_COMPONENT_LIBRARY_DISPLAY_NAME "${DIST_NAME} Development Libraries" ) 154 | set ( CPACK_COMPONENT_LIBRARY_DESCRIPTION 155 | "Static and import libraries needed for development. Installed into ${INSTALL_LIB} or ${INSTALL_BIN}." ) 156 | macro ( install_library ) 157 | foreach ( _file ${ARGN} ) 158 | if ( INSTALL_VERSION ) 159 | set_target_properties ( ${_file} PROPERTIES VERSION ${DIST_VERSION} 160 | SOVERSION ${DIST_VERSION} ) 161 | endif () 162 | install ( TARGETS ${_file} 163 | RUNTIME DESTINATION ${INSTALL_BIN} COMPONENT Runtime 164 | LIBRARY DESTINATION ${INSTALL_LIB} COMPONENT Runtime 165 | ARCHIVE DESTINATION ${INSTALL_LIB} COMPONENT Library ) 166 | endforeach() 167 | endmacro () 168 | 169 | # helper function for various install_* functions, for PATTERN/REGEX args. 170 | macro ( _complete_install_args ) 171 | if ( NOT("${_ARG_PATTERN}" STREQUAL "") ) 172 | set ( _ARG_PATTERN PATTERN ${_ARG_PATTERN} ) 173 | endif () 174 | if ( NOT("${_ARG_REGEX}" STREQUAL "") ) 175 | set ( _ARG_REGEX REGEX ${_ARG_REGEX} ) 176 | endif () 177 | endmacro () 178 | 179 | # install_header ( files/directories [INTO destination] ) 180 | # Install a directories or files into header destination. 181 | # USE: install_header ( lua.h luaconf.h ) or install_header ( GL ) 182 | # USE: install_header ( mylib.h INTO mylib ) 183 | # For directories, supports optional PATTERN/REGEX arguments like install(). 184 | set ( CPACK_COMPONENT_HEADER_DISPLAY_NAME "${DIST_NAME} Development Headers" ) 185 | set ( CPACK_COMPONENT_HEADER_DESCRIPTION 186 | "Headers needed for development. Installed into ${INSTALL_INC}." ) 187 | macro ( install_header ) 188 | parse_arguments ( _ARG "INTO;PATTERN;REGEX" "" ${ARGN} ) 189 | _complete_install_args() 190 | foreach ( _file ${_ARG_DEFAULT_ARGS} ) 191 | if ( IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/${_file}" ) 192 | install ( DIRECTORY ${_file} DESTINATION ${INSTALL_INC}/${_ARG_INTO} 193 | COMPONENT Header ${_ARG_PATTERN} ${_ARG_REGEX} ) 194 | else () 195 | install ( FILES ${_file} DESTINATION ${INSTALL_INC}/${_ARG_INTO} 196 | COMPONENT Header ) 197 | endif () 198 | endforeach() 199 | endmacro () 200 | 201 | # install_data ( files/directories [INTO destination] ) 202 | # This installs additional data files or directories. 203 | # USE: install_data ( extra data.dat ) 204 | # USE: install_data ( image1.png image2.png INTO images ) 205 | # For directories, supports optional PATTERN/REGEX arguments like install(). 206 | set ( CPACK_COMPONENT_DATA_DISPLAY_NAME "${DIST_NAME} Data" ) 207 | set ( CPACK_COMPONENT_DATA_DESCRIPTION 208 | "Application data. Installed into ${INSTALL_DATA}." ) 209 | macro ( install_data ) 210 | parse_arguments ( _ARG "INTO;PATTERN;REGEX" "" ${ARGN} ) 211 | _complete_install_args() 212 | foreach ( _file ${_ARG_DEFAULT_ARGS} ) 213 | if ( IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/${_file}" ) 214 | install ( DIRECTORY ${_file} 215 | DESTINATION ${INSTALL_DATA}/${_ARG_INTO} 216 | COMPONENT Data ${_ARG_PATTERN} ${_ARG_REGEX} ) 217 | else () 218 | install ( FILES ${_file} DESTINATION ${INSTALL_DATA}/${_ARG_INTO} 219 | COMPONENT Data ) 220 | endif () 221 | endforeach() 222 | endmacro () 223 | 224 | # INSTALL_DOC ( files/directories [INTO destination] ) 225 | # This installs documentation content 226 | # USE: install_doc ( doc/ doc.pdf ) 227 | # USE: install_doc ( index.html INTO html ) 228 | # For directories, supports optional PATTERN/REGEX arguments like install(). 229 | set ( CPACK_COMPONENT_DOCUMENTATION_DISPLAY_NAME "${DIST_NAME} Documentation" ) 230 | set ( CPACK_COMPONENT_DOCUMENTATION_DESCRIPTION 231 | "Application documentation. Installed into ${INSTALL_DOC}." ) 232 | macro ( install_doc ) 233 | parse_arguments ( _ARG "INTO;PATTERN;REGEX" "" ${ARGN} ) 234 | _complete_install_args() 235 | foreach ( _file ${_ARG_DEFAULT_ARGS} ) 236 | if ( IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/${_file}" ) 237 | install ( DIRECTORY ${_file} DESTINATION ${INSTALL_DOC}/${_ARG_INTO} 238 | COMPONENT Documentation ${_ARG_PATTERN} ${_ARG_REGEX} ) 239 | else () 240 | install ( FILES ${_file} DESTINATION ${INSTALL_DOC}/${_ARG_INTO} 241 | COMPONENT Documentation ) 242 | endif () 243 | endforeach() 244 | endmacro () 245 | 246 | # install_example ( files/directories [INTO destination] ) 247 | # This installs additional examples 248 | # USE: install_example ( examples/ exampleA ) 249 | # USE: install_example ( super_example super_data INTO super) 250 | # For directories, supports optional PATTERN/REGEX argument like install(). 251 | set ( CPACK_COMPONENT_EXAMPLE_DISPLAY_NAME "${DIST_NAME} Examples" ) 252 | set ( CPACK_COMPONENT_EXAMPLE_DESCRIPTION 253 | "Examples and their associated data. Installed into ${INSTALL_EXAMPLE}." ) 254 | macro ( install_example ) 255 | parse_arguments ( _ARG "INTO;PATTERN;REGEX" "" ${ARGN} ) 256 | _complete_install_args() 257 | foreach ( _file ${_ARG_DEFAULT_ARGS} ) 258 | if ( IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/${_file}" ) 259 | install ( DIRECTORY ${_file} DESTINATION ${INSTALL_EXAMPLE}/${_ARG_INTO} 260 | COMPONENT Example ${_ARG_PATTERN} ${_ARG_REGEX} ) 261 | else () 262 | install ( FILES ${_file} DESTINATION ${INSTALL_EXAMPLE}/${_ARG_INTO} 263 | COMPONENT Example ) 264 | endif () 265 | endforeach() 266 | endmacro () 267 | 268 | # install_test ( files/directories [INTO destination] ) 269 | # This installs tests and test files, DOES NOT EXECUTE TESTS 270 | # USE: install_test ( my_test data.sql ) 271 | # USE: install_test ( feature_x_test INTO x ) 272 | # For directories, supports optional PATTERN/REGEX argument like install(). 273 | set ( CPACK_COMPONENT_TEST_DISPLAY_NAME "${DIST_NAME} Tests" ) 274 | set ( CPACK_COMPONENT_TEST_DESCRIPTION 275 | "Tests and associated data. Installed into ${INSTALL_TEST}." ) 276 | macro ( install_test ) 277 | parse_arguments ( _ARG "INTO;PATTERN;REGEX" "" ${ARGN} ) 278 | _complete_install_args() 279 | foreach ( _file ${_ARG_DEFAULT_ARGS} ) 280 | if ( IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/${_file}" ) 281 | install ( DIRECTORY ${_file} DESTINATION ${INSTALL_TEST}/${_ARG_INTO} 282 | COMPONENT Test ${_ARG_PATTERN} ${_ARG_REGEX} ) 283 | else () 284 | install ( FILES ${_file} DESTINATION ${INSTALL_TEST}/${_ARG_INTO} 285 | COMPONENT Test ) 286 | endif () 287 | endforeach() 288 | endmacro () 289 | 290 | # install_foo ( files/directories [INTO destination] ) 291 | # This installs optional or otherwise unneeded content 292 | # USE: install_foo ( etc/ example.doc ) 293 | # USE: install_foo ( icon.png logo.png INTO icons) 294 | # For directories, supports optional PATTERN/REGEX argument like install(). 295 | set ( CPACK_COMPONENT_OTHER_DISPLAY_NAME "${DIST_NAME} Unspecified Content" ) 296 | set ( CPACK_COMPONENT_OTHER_DESCRIPTION 297 | "Other unspecified content. Installed into ${INSTALL_FOO}." ) 298 | macro ( install_foo ) 299 | parse_arguments ( _ARG "INTO;PATTERN;REGEX" "" ${ARGN} ) 300 | _complete_install_args() 301 | foreach ( _file ${_ARG_DEFAULT_ARGS} ) 302 | if ( IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/${_file}" ) 303 | install ( DIRECTORY ${_file} DESTINATION ${INSTALL_FOO}/${_ARG_INTO} 304 | COMPONENT Other ${_ARG_PATTERN} ${_ARG_REGEX} ) 305 | else () 306 | install ( FILES ${_file} DESTINATION ${INSTALL_FOO}/${_ARG_INTO} 307 | COMPONENT Other ) 308 | endif () 309 | endforeach() 310 | endmacro () 311 | 312 | ## CTest defaults 313 | 314 | ## CPack defaults 315 | set ( CPACK_GENERATOR "ZIP" ) 316 | set ( CPACK_STRIP_FILES TRUE ) 317 | set ( CPACK_PACKAGE_NAME "${DIST_NAME}" ) 318 | set ( CPACK_PACKAGE_VERSION "${DIST_VERSION}") 319 | set ( CPACK_PACKAGE_VENDOR "LuaDist" ) 320 | set ( CPACK_COMPONENTS_ALL Runtime Library Header Data Documentation Example Other ) 321 | include ( CPack ) 322 | -------------------------------------------------------------------------------- /lua/path/win32/ffi/fs.lua: -------------------------------------------------------------------------------- 1 | ------------------------------------------------------------------ 2 | -- 3 | -- Author: Alexey Melnichuk 4 | -- 5 | -- Copyright (C) 2013-2016 Alexey Melnichuk 6 | -- 7 | -- Licensed according to the included 'LICENCE' document 8 | -- 9 | -- This file is part of lua-path library. 10 | -- 11 | ------------------------------------------------------------------ 12 | 13 | local ffi = require "ffi" 14 | local types = require "path.win32.ffi.types" 15 | 16 | ffi.cdef[[ 17 | DWORD __stdcall GetCurrentDirectoryA( DWORD nBufferLength, CHAR* lpBuffer); 18 | DWORD __stdcall GetCurrentDirectoryW( DWORD nBufferLength, CHAR* lpBuffer); 19 | BOOL __stdcall SetCurrentDirectoryA(const CHAR* lpPathName); 20 | BOOL __stdcall SetCurrentDirectoryW(const CHAR* lpPathName); 21 | DWORD __stdcall GetTempPathA(DWORD n, CHAR* buf); 22 | DWORD __stdcall GetTempPathW(DWORD n, CHAR* buf); 23 | DWORD __stdcall GetFileAttributesExA(const CHAR* lpFileName, GET_FILEEX_INFO_LEVELS fInfoLevelId, void* lpFileInformation); 24 | DWORD __stdcall GetFileAttributesExW(const CHAR* lpFileName, GET_FILEEX_INFO_LEVELS fInfoLevelId, void* lpFileInformation); 25 | BOOL __stdcall CopyFileA(const CHAR* src, const CHAR* dst, BOOL flag); 26 | BOOL __stdcall CopyFileW(const CHAR* src, const CHAR* dst, BOOL flag); 27 | HANDLE __stdcall FindFirstFileA(const CHAR* pattern, WIN32_FIND_DATAA* fd); 28 | HANDLE __stdcall FindFirstFileW(const CHAR* pattern, WIN32_FIND_DATAW* fd); 29 | int __stdcall FindNextFileA(HANDLE ff, WIN32_FIND_DATAA* fd); 30 | int __stdcall FindNextFileW(HANDLE ff, WIN32_FIND_DATAW* fd); 31 | int __stdcall FindClose(HANDLE ff); 32 | DWORD __stdcall GetLastError(); 33 | BOOL __stdcall RemoveDirectoryA(const CHAR* src); 34 | BOOL __stdcall RemoveDirectoryW(const CHAR* src); 35 | BOOL __stdcall MoveFileExA(const CHAR* src, const CHAR* dst, DWORD flags); 36 | BOOL __stdcall MoveFileExW(const CHAR* src, const CHAR* dst, DWORD flags); 37 | BOOL __stdcall DeleteFileA(const CHAR* src); 38 | BOOL __stdcall DeleteFileW(const CHAR* src); 39 | BOOL __stdcall CreateDirectoryA(const CHAR* src, void* lpSecurityAttributes); 40 | BOOL __stdcall CreateDirectoryW(const CHAR* src, void* lpSecurityAttributes); 41 | 42 | HANDLE __stdcall CreateFileA(const CHAR* lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, void* lpSecurityAttributes, 43 | DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile 44 | ); 45 | 46 | HANDLE __stdcall CreateFileW(const CHAR* lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, void* lpSecurityAttributes, 47 | DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile 48 | ); 49 | 50 | BOOL __stdcall SetFileTime(HANDLE hFile, const FILETIME *lpCreationTime, const FILETIME *lpLastAccessTime,const FILETIME *lpLastWriteTime); 51 | 52 | BOOL __stdcall CloseHandle(HANDLE hObject); 53 | 54 | DWORD __stdcall FormatMessageA(DWORD dwFlags, void* lpSource, DWORD dwMessageId, DWORD dwLanguageId, LPVOID lpBuffer, DWORD nSize, void *Arguments); 55 | DWORD __stdcall FormatMessageW(DWORD dwFlags, void* lpSource, DWORD dwMessageId, DWORD dwLanguageId, LPVOID lpBuffer, DWORD nSize, void *Arguments); 56 | HLOCAL __stdcall LocalFree( HLOCAL hMem ); 57 | 58 | BOOL __stdcall DeviceIoControl(HANDLE hDevice, DWORD dwIoControlCode, void* lpInBuffer, DWORD nInBufferSize, void* lpOutBuffer, DWORD nOutBufferSize, DWORD* lpBytesReturned, void* lpOverlapped); 59 | ]] 60 | 61 | local C = ffi.C 62 | local CTYPES = types.CTYPES 63 | local CTYPE2LUA = types.CTYPE2LUA 64 | local WIN32_FIND_DATAA = CTYPES.WIN32_FIND_DATAA 65 | local WIN32_FIND_DATAW = CTYPES.WIN32_FIND_DATAW 66 | local FILETIME = CTYPES.FILETIME 67 | local INVALID_HANDLE = types.INVALID_HANDLE 68 | local NULL = ffi.cast("void*", 0) 69 | local NULLSTR = ffi.cast("CHAR*", NULL) 70 | 71 | local function GetCurrentDirectory(u) 72 | local n = (u and C.GetCurrentDirectoryW or C.GetCurrentDirectoryA)(0,NULLSTR) 73 | if n == 0 then 74 | local err = C.GetLastError() 75 | return nil, err 76 | end 77 | 78 | local buf = ffi.new(CTYPES.VLA_CHAR, u and 2*n or n) 79 | 80 | n = (u and C.GetCurrentDirectoryW or C.GetCurrentDirectoryA)(n, buf) 81 | if n == 0 then 82 | local err = C.GetLastError() 83 | return nil, err 84 | end 85 | return ffi.string(buf, u and 2*n or n) 86 | end 87 | 88 | local function SetCurrentDirectory(u, P) 89 | local ret 90 | if u then ret = C.SetCurrentDirectoryW(P .. "\0") 91 | else ret = C.SetCurrentDirectoryA(P) end 92 | if ret == 0 then 93 | local err = C.GetLastError() 94 | return nil, err 95 | end 96 | return true 97 | end 98 | 99 | local function GetTempPath(u) 100 | local n = (u and C.GetTempPathW or C.GetTempPathA)(0,NULLSTR) 101 | if n == 0 then 102 | local err = C.GetLastError() 103 | return nil, err 104 | end 105 | 106 | local buf = ffi.new(CTYPES.VLA_CHAR, u and 2*n or n) 107 | 108 | n = (u and C.GetTempPathW or C.GetTempPathA)(n, buf) 109 | if n == 0 then 110 | local err = C.GetLastError() 111 | return nil, err 112 | end 113 | return ffi.string(buf, u and 2*n or n) 114 | end 115 | 116 | local function GetFileAttributesEx(u, P) 117 | local ret, info 118 | if u then 119 | info = WIN32_FIND_DATAW() 120 | ret = C.GetFileAttributesExW(P .. "\0", C.GetFileExInfoStandard, info) 121 | else 122 | info = WIN32_FIND_DATAA() 123 | ret = C.GetFileAttributesExA(P, C.GetFileExInfoStandard, info) 124 | end 125 | if ret == 0 then 126 | local err = C.GetLastError() 127 | return nil, err 128 | end 129 | return info 130 | end 131 | 132 | local function CopyFile(u, src, dst, flag) 133 | local ret 134 | if u then ret = C.CopyFileW(src .. "\0", dst .. "\0", flag and 1 or 0) 135 | else ret = C.CopyFileA(src, dst, flag and 1 or 0) end 136 | if ret == 0 then 137 | local err = C.GetLastError() 138 | return nil, err 139 | end 140 | return true 141 | end 142 | 143 | local function FindFirstFile(u, P) 144 | local ret, fd, err 145 | if u then 146 | fd = WIN32_FIND_DATAW() 147 | ret = C.FindFirstFileW(P .. "\0", fd) 148 | else 149 | fd = WIN32_FIND_DATAA() 150 | ret = C.FindFirstFileA(P, fd) 151 | end 152 | if ret == INVALID_HANDLE then 153 | local err = C.GetLastError() 154 | return nil, err 155 | end 156 | ffi.gc(ret, C.FindClose) 157 | return ret, fd 158 | end 159 | 160 | local function FindNextFile(u, h, fd) 161 | local ret 162 | if u then ret = C.FindNextFileW(h, fd) 163 | else ret = C.FindNextFileA(h, fd) end 164 | return ret 165 | end 166 | 167 | local function FindClose(h) 168 | C.FindClose(ffi.gc(h, nil)) 169 | end 170 | 171 | local function RemoveDirectory(u, src) 172 | local ret 173 | if u then ret = C.RemoveDirectoryW(src .. "\0") 174 | else ret = C.RemoveDirectoryA(src) end 175 | if ret == 0 then 176 | local err = C.GetLastError() 177 | return nil, err 178 | end 179 | return true 180 | end 181 | 182 | local function DeleteFile(u, src) 183 | local ret 184 | if u then ret = C.DeleteFileW(src .. "\0") 185 | else ret = C.DeleteFileA(src) end 186 | if ret == 0 then 187 | local err = C.GetLastError() 188 | return nil, err 189 | end 190 | return true 191 | end 192 | 193 | local function MoveFileEx(u, src, dst, flag) 194 | local ret 195 | if u then ret = C.MoveFileExW(src .. "\0", dst .. "\0", flag and flag or 0) 196 | else ret = C.MoveFileExA(src, dst, flag and flag or 0) end 197 | if ret == 0 then 198 | local err = C.GetLastError() 199 | return nil, err 200 | end 201 | return true 202 | end 203 | 204 | local function CreateDirectory(u, src) 205 | local ret 206 | if u then ret = C.CreateDirectoryW(src .. "\0",NULL) 207 | else ret = C.CreateDirectoryA(src,NULL) end 208 | if ret == 0 then 209 | local err = C.GetLastError() 210 | return nil, err 211 | end 212 | return true 213 | end 214 | 215 | local function CreateFile(u, P, access, share, sec, mode, attr, template) 216 | local p = P 217 | if u then p = p .. "\0" end 218 | 219 | local h = (u and C.CreateFileW or C.CreateFileA)( 220 | p, access, share, sec or NULL, mode, attr, template or NULL 221 | ); 222 | 223 | if INVALID_HANDLE == h then 224 | local err = C.GetLastError() 225 | return nil, err 226 | end 227 | 228 | return ffi.gc(h, C.CloseHandle) 229 | end 230 | 231 | local function CloseHandle(h) 232 | return C.CloseHandle(ffi.gc(h, nil)) 233 | end 234 | 235 | local function newft(t) 236 | if not t then return ffi.cast("FILETIME*", NULL) end 237 | local v = FILETIME() 238 | v.dwLowDateTime, v.dwHighDateTime = t[1], t[2] 239 | return v 240 | end 241 | 242 | local function SetFileTime(h, c, a, m) 243 | local ctime, atime, mtime = newft(c), newft(a), newft(m) 244 | local ret = C.SetFileTime(h, ctime, atime, mtime) 245 | if ret ~= 0 then return true end 246 | return nil, C.GetLastError() 247 | end 248 | 249 | local FORMAT_MESSAGE_ALLOCATE_BUFFER = 0x00000100 250 | local FORMAT_MESSAGE_IGNORE_INSERTS = 0x00000200 251 | local FORMAT_MESSAGE_FROM_STRING = 0x00000400 252 | local FORMAT_MESSAGE_FROM_HMODULE = 0x00000800 253 | local FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000 254 | local FORMAT_MESSAGE_ARGUMENT_ARRAY = 0x00002000 255 | local FORMAT_MESSAGE_MAX_WIDTH_MASK = 0x000000FF 256 | 257 | local function ErrorMessage(u, dwErr, lang) 258 | local lpMsgBuf = ffi.new("LPVOID[1]", 0); 259 | lang = lang or 0 260 | local ret = (u and C.FormatMessageW or C.FormatMessageA)( 261 | FORMAT_MESSAGE_ALLOCATE_BUFFER + FORMAT_MESSAGE_FROM_SYSTEM + FORMAT_MESSAGE_IGNORE_INSERTS, 262 | NULL, dwErr, lang, lpMsgBuf, 0, NULL 263 | ); 264 | 265 | if ret == 0 then 266 | local err = C.GetLastError() 267 | return "", err 268 | end 269 | 270 | local str = ffi.cast(CTYPES.PCHAR, lpMsgBuf[0]) 271 | str = ffi.string(str, u and 2 * ret or ret); 272 | ret = C.LocalFree(lpMsgBuf[0]); 273 | return str; 274 | end 275 | 276 | local function DeviceIoControl(h, code, inBuffer, inBufferSize, outBuffer, outBufferSize) 277 | if inBuffer == nil then inBuffer, inBufferSize = NULL, 0 end 278 | if outBuffer == nil then outBuffer, outBufferSize = NULL, 0 end 279 | local dwTmp = ffi.new("DWORD[1]", 0) 280 | local ret = C.DeviceIoControl(h, code, 281 | inBuffer, inBufferSize, outBuffer, outBufferSize, 282 | dwTmp, NULL 283 | ) 284 | if ret == 0 then 285 | local err = C.GetLastError() 286 | return nil, err 287 | end 288 | 289 | return ret, dwTmp[0] 290 | end 291 | 292 | local FILE_FLAG_NO_BUFFERING = 0x20000000 293 | local FILE_ATTRIBUTE_NORMAL = 0x00000080 294 | local FILE_SHARE_READ = 0x00000001 295 | local FILE_SHARE_WRITE = 0x00000002 296 | local OPEN_EXISTING = 3 297 | local IOCTL_STORAGE_GET_DEVICE_NUMBER = 0x2D1080 298 | 299 | local function DiskNumber(u, P) 300 | local p 301 | if u then p = "\\\0\\\0.\0\\\0" .. P .. "\0" 302 | else p = "\\\\.\\" .. P end 303 | 304 | -- Open partition 305 | local hPart, err = CreateFile(u, p, 0, 306 | FILE_SHARE_READ + FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 307 | FILE_ATTRIBUTE_NORMAL + FILE_FLAG_NO_BUFFERING, NULL 308 | ); 309 | 310 | if not hPart then return nil, err end 311 | 312 | local Info = CTYPES.STORAGE_DEVICE_NUMBER(); 313 | local Info_size = ffi.sizeof(CTYPES.STORAGE_DEVICE_NUMBER) 314 | 315 | local ret, dwTmp = DeviceIoControl(hPart, IOCTL_STORAGE_GET_DEVICE_NUMBER, 316 | NULL, 0, Info, Info_size 317 | ) 318 | if not ret then err = dwTmp end 319 | if dwTmp ~= Info_size then ret, err = nil, C.GetLastError() end 320 | 321 | CloseHandle(hPart) 322 | if not ret then return nil, err end 323 | return { 324 | Info.DeviceType, 325 | Info.DeviceNumber, 326 | Info.PartitionNumber, 327 | } 328 | end 329 | 330 | return { 331 | A = { 332 | GetCurrentDirectory = function(...) return GetCurrentDirectory(false, ...) end; 333 | SetCurrentDirectory = function(...) return SetCurrentDirectory(false, ...) end; 334 | GetTempPath = function(...) return GetTempPath (false, ...) end; 335 | GetFileAttributesEx = function(...) return GetFileAttributesEx(false, ...) end; 336 | CopyFile = function(...) return CopyFile (false, ...) end; 337 | FindFirstFile = function(...) return FindFirstFile (false, ...) end; 338 | FindNextFile = function(...) return FindNextFile (false, ...) end; 339 | RemoveDirectory = function(...) return RemoveDirectory (false, ...) end; 340 | DeleteFile = function(...) return DeleteFile (false, ...) end; 341 | CreateDirectory = function(...) return CreateDirectory (false, ...) end; 342 | CreateFile = function(...) return CreateFile (false, ...) end; 343 | MoveFileEx = function(...) return MoveFileEx (false, ...) end; 344 | ErrorMessage = function(...) return ErrorMessage (false, ...) end; 345 | DiskNumber = function(...) return DiskNumber (false, ...) end; 346 | FindClose = FindClose; 347 | CloseHandle = CloseHandle; 348 | SetFileTime = SetFileTime; 349 | DeviceIoControl = DeviceIoControl; 350 | WIN32_FIND_DATA2TABLE = CTYPE2LUA.WIN32_FIND_DATAA; 351 | DIR_SEP = "\\"; 352 | ANY_MASK = "*"; 353 | }; 354 | W = { 355 | GetCurrentDirectory = function(...) return GetCurrentDirectory(true, ...) end; 356 | SetCurrentDirectory = function(...) return SetCurrentDirectory(true, ...) end; 357 | GetTempPath = function(...) return GetTempPath (true, ...) end; 358 | GetFileAttributesEx = function(...) return GetFileAttributesEx(true, ...) end; 359 | CopyFile = function(...) return CopyFile (true, ...) end; 360 | FindFirstFile = function(...) return FindFirstFile (true, ...) end; 361 | FindNextFile = function(...) return FindNextFile (true, ...) end; 362 | RemoveDirectory = function(...) return RemoveDirectory (true, ...) end; 363 | DeleteFile = function(...) return DeleteFile (true, ...) end; 364 | CreateDirectory = function(...) return CreateDirectory (true, ...) end; 365 | CreateFile = function(...) return CreateFile (true, ...) end; 366 | MoveFileEx = function(...) return MoveFileEx (true, ...) end; 367 | ErrorMessage = function(...) return ErrorMessage (true, ...) end; 368 | DiskNumber = function(...) return DiskNumber (true, ...) end; 369 | FindClose = FindClose; 370 | CloseHandle = CloseHandle; 371 | SetFileTime = SetFileTime; 372 | DeviceIoControl = DeviceIoControl; 373 | WIN32_FIND_DATA2TABLE = CTYPE2LUA.WIN32_FIND_DATAW; 374 | DIR_SEP = "\\\000"; 375 | ANY_MASK = "*\000"; 376 | }; 377 | } -------------------------------------------------------------------------------- /lua/path.lua: -------------------------------------------------------------------------------- 1 | ------------------------------------------------------------------ 2 | -- 3 | -- Author: Alexey Melnichuk 4 | -- 5 | -- Copyright (C) 2013-2016 Alexey Melnichuk 6 | -- 7 | -- Licensed according to the included 'LICENCE' document 8 | -- 9 | -- This file is part of lua-path library. 10 | -- 11 | ------------------------------------------------------------------ 12 | 13 | local package = require "package" 14 | local string = require "string" 15 | local table = require "table" 16 | local os = require "os" 17 | local io = require "io" 18 | 19 | local USE_ALIEN = true 20 | local USE_FFI = true 21 | local USE_AFX = true 22 | 23 | local DIR_SEP = package.config:sub(1,1) 24 | local IS_WINDOWS = DIR_SEP == '\\' 25 | 26 | local PATH = {} 27 | 28 | PATH.DIR_SEP = DIR_SEP 29 | PATH.IS_WINDOWS = IS_WINDOWS 30 | 31 | -- 32 | -- PATH manipulation 33 | 34 | function PATH:unquote(P) 35 | if P:sub(1,1) == '"' and P:sub(-1,-1) == '"' then 36 | return (P:sub(2,-2)) 37 | end 38 | return P 39 | end 40 | 41 | function PATH:quote(P) 42 | if P:find("%s") then 43 | return '"' .. P .. '"' 44 | end 45 | return P 46 | end 47 | 48 | function PATH:has_dir_end(P) 49 | return (string.find(P, '[\\/]$')) and true 50 | end 51 | 52 | function PATH:remove_dir_end(P) 53 | return (string.gsub(P, '[\\/]+$', '')) 54 | end 55 | 56 | function PATH:ensure_dir_end(P) 57 | return self:remove_dir_end(P) .. self.DIR_SEP 58 | end 59 | 60 | function PATH:isunc(P) 61 | return (string.sub(P, 1, 2) == (self.DIR_SEP .. self.DIR_SEP)) and P 62 | end 63 | 64 | function PATH:normolize_sep(P) 65 | return (string.gsub(P, '\\', self.DIR_SEP):gsub('/', self.DIR_SEP)) 66 | end 67 | 68 | PATH.normalize_sep = PATH.normolize_sep 69 | 70 | function PATH:normolize(P) 71 | P = self:normolize_sep(P) 72 | local DIR_SEP = self.DIR_SEP 73 | 74 | local is_unc = self:isunc(P) 75 | while true do -- `/./` => `/` 76 | local n P,n = string.gsub(P, DIR_SEP .. '%.' .. DIR_SEP, DIR_SEP) 77 | if n == 0 then break end 78 | end 79 | while true do -- `//` => `/` 80 | local n P,n = string.gsub(P, DIR_SEP .. DIR_SEP, DIR_SEP) 81 | if n == 0 then break end 82 | end 83 | P = string.gsub(P, DIR_SEP .. '%.$', '') 84 | if (not IS_WINDOWS) and (P == '') then P = '/' end 85 | 86 | if is_unc then P = DIR_SEP .. P end 87 | 88 | local root, path = nil, P 89 | if is_unc then 90 | root, path = self:splitroot(P) 91 | end 92 | 93 | path = self:ensure_dir_end(path) 94 | while true do 95 | local first, last = string.find(path, DIR_SEP .. "[^".. DIR_SEP .. "]+" .. DIR_SEP .. '%.%.' .. DIR_SEP) 96 | if not first then break end 97 | path = string.sub(path, 1, first) .. string.sub(path, last+1) 98 | end 99 | P = path 100 | 101 | if root then -- unc 102 | assert(is_unc) 103 | P = P:gsub( '%.%.?' .. DIR_SEP , '') 104 | P = DIR_SEP .. DIR_SEP .. self:join(root, P) 105 | elseif self.IS_WINDOWS then 106 | -- c:\..\foo => c:\foo 107 | -- \..\foo => \foo 108 | local root, path = self:splitroot(P) 109 | if root ~= '' or P:sub(1,1) == DIR_SEP then 110 | path = path:gsub( '%.%.?' .. DIR_SEP , '') 111 | P = self:join(root, path) 112 | end 113 | end 114 | 115 | if self.IS_WINDOWS and #P <= 3 and P:sub(2,2) == ':' then -- c: => c:\ or c:\ => c:\ 116 | if #P == 2 then return P .. self.DIR_SEP end 117 | return P 118 | end 119 | 120 | if (not self.IS_WINDOWS) and (P == DIR_SEP) then return '/' end 121 | return self:remove_dir_end(P) 122 | end 123 | 124 | PATH.normalize = PATH.normolize 125 | 126 | function PATH:join_(P1, P2) 127 | local ch = P2:sub(1,1) 128 | if (ch == '\\') or (ch == '/') then 129 | return self:remove_dir_end(P1) .. P2 130 | end 131 | return self:ensure_dir_end(P1) .. P2 132 | end 133 | 134 | function PATH:join(...) 135 | local t,n = {...}, select('#', ...) 136 | local r = t[1] 137 | for i = 2, #t do 138 | if self:isfullpath(t[i]) then 139 | r = t[i] 140 | else 141 | r = self:join_(r,t[i]) 142 | end 143 | end 144 | return r 145 | end 146 | 147 | function PATH:splitext(P) 148 | local s1,s2 = string.match(P,"(.-[^\\/.])(%.[^\\/.]*)$") 149 | if s1 then return s1,s2 end 150 | return P, '' 151 | end 152 | 153 | function PATH:splitpath(P) 154 | return string.match(P,"^(.-)[\\/]?([^\\/]*)$") 155 | end 156 | 157 | function PATH:splitroot(P) 158 | if self.IS_WINDOWS then 159 | if self:isunc(P) then 160 | return string.match(P, [[^\\([^\/]+)[\]?(.*)$]]) 161 | end 162 | if string.sub(P,2,2) == ':' then 163 | return string.sub(P,1,2), string.sub(P,4) 164 | end 165 | return '', P 166 | else 167 | if string.sub(P,1,1) == '/' then 168 | return string.match(P,[[^/([^\/]+)[/]?(.*)$]]) 169 | end 170 | return '', P 171 | end 172 | end 173 | 174 | function PATH:splitdrive(P) 175 | if self.IS_WINDOWS then 176 | return self:splitroot(P) 177 | end 178 | return '', P 179 | end 180 | 181 | function PATH:basename(P) 182 | local s1,s2 = self:splitpath(P) 183 | return s2 184 | end 185 | 186 | function PATH:dirname(P) 187 | return (self:splitpath(P)) 188 | end 189 | 190 | function PATH:extension(P) 191 | local s1,s2 = self:splitext(P) 192 | return s2 193 | end 194 | 195 | function PATH:root(P) 196 | return (self:splitroot(P)) 197 | end 198 | 199 | function PATH:isfullpath(P) 200 | return (self:root(P) ~= '') and P 201 | end 202 | 203 | function PATH:user_home() 204 | if IS_WINDOWS then 205 | return os.getenv('USERPROFILE') or PATH:join(os.getenv('HOMEDRIVE'), os.getenv('HOMEPATH')) 206 | end 207 | return os.getenv('HOME') 208 | end 209 | 210 | local function prequire(m) 211 | local ok, err = pcall(require, m) 212 | if not ok then return nil, err end 213 | return err 214 | end 215 | 216 | local fs = prequire "path.fs" 217 | 218 | if fs then 219 | 220 | -- 221 | -- PATH based on system 222 | 223 | local function assert_system(self) 224 | if PATH.IS_WINDOWS then assert(self.IS_WINDOWS) return end 225 | assert(not self.IS_WINDOWS) 226 | end 227 | 228 | if fs.flags then 229 | function PATH:flags(P, ...) 230 | assert_system(self) 231 | P = self:fullpath(P) 232 | return fs.flags(P, ...) 233 | end 234 | end 235 | 236 | function PATH:tmpdir() 237 | assert_system(self) 238 | return self:remove_dir_end(fs.tmpdir()) 239 | end 240 | 241 | function PATH:tmpname() 242 | local P = os.tmpname() 243 | if self:dirname(P) == '' then 244 | P = self:join(self:tmpdir(), P) 245 | end 246 | return P 247 | end 248 | 249 | function PATH:size(P) 250 | assert_system(self) 251 | return fs.size(P) 252 | end 253 | 254 | function PATH:fullpath(P) 255 | if not self:isfullpath(P) then 256 | P = self:normolize_sep(P) 257 | local ch1, ch2 = P:sub(1,1), P:sub(2,2) 258 | if ch1 == '~' then -- ~\temp 259 | P = self:join(self:user_home(), P:sub(2)) 260 | elseif self.IS_WINDOWS and (ch1 == self.DIR_SEP) then -- \temp => c:\temp 261 | local root = self:root(self:currentdir()) 262 | P = self:join(root, P) 263 | else 264 | P = self:join(self:currentdir(), P) 265 | end 266 | end 267 | 268 | return self:normolize(P) 269 | end 270 | 271 | function PATH:attrib(P, ...) 272 | assert_system(self) 273 | return fs.attributes(P, ...) 274 | end 275 | 276 | function PATH:exists(P) 277 | assert_system(self) 278 | return fs.exists(self:fullpath(P)) 279 | end 280 | 281 | function PATH:isdir(P) 282 | assert_system(self) 283 | return fs.isdir(self:fullpath(P)) 284 | end 285 | 286 | function PATH:isfile(P) 287 | assert_system(self) 288 | return fs.isfile(self:fullpath(P)) 289 | end 290 | 291 | function PATH:islink(P) 292 | assert_system(self) 293 | return fs.islink(self:fullpath(P)) 294 | end 295 | 296 | function PATH:ctime(P) 297 | assert_system(self) 298 | return fs.ctime(self:fullpath(P)) 299 | end 300 | 301 | function PATH:mtime(P) 302 | assert_system(self) 303 | return fs.mtime(self:fullpath(P)) 304 | end 305 | 306 | function PATH:atime(P) 307 | assert_system(self) 308 | return fs.atime(self:fullpath(P)) 309 | end 310 | 311 | function PATH:touch(P, ...) 312 | assert_system(self) 313 | return fs.touch(self:fullpath(P), ...) 314 | end 315 | 316 | function PATH:currentdir() 317 | assert_system(self) 318 | return self:normolize(fs.currentdir()) 319 | end 320 | 321 | function PATH:chdir(P) 322 | assert_system(self) 323 | return fs.chdir(self:fullpath(P)) 324 | end 325 | 326 | function PATH:isempty(P) 327 | assert_system(self) 328 | local ok, err = fs.each_impl{ 329 | file = self:ensure_dir_end(P), 330 | callback = function() return 'pass' end; 331 | } 332 | if err then return nil, err end 333 | return ok ~= 'pass' 334 | end 335 | 336 | local date = prequire "date" 337 | if date then 338 | local function make_getfiletime_as_date(fn) 339 | if date then 340 | return function(...) 341 | local t,e = fn(...) 342 | if not t then return nil, e end 343 | return date(t) 344 | end 345 | end 346 | end 347 | 348 | PATH.cdate = make_getfiletime_as_date( PATH.ctime ); 349 | PATH.mdate = make_getfiletime_as_date( PATH.mtime ); 350 | PATH.adate = make_getfiletime_as_date( PATH.atime ); 351 | end 352 | 353 | function PATH:mkdir(P) 354 | assert_system(self) 355 | local P = self:fullpath(P) 356 | if self:exists(P) then return self:isdir(P) end 357 | local p = '' 358 | P = self:ensure_dir_end(P) 359 | for str in string.gmatch(P, '.-' .. self.DIR_SEP) do 360 | p = p .. str 361 | if self:exists(p) then 362 | if not self:isdir(p) then 363 | return nil, 'can not create ' .. p 364 | end 365 | else 366 | if IS_WINDOWS or p ~= DIR_SEP then 367 | local ok, err = fs.mkdir(self:remove_dir_end(p)) 368 | if not ok then return nil, err .. ' ' .. p end 369 | end 370 | end 371 | end 372 | return P 373 | end 374 | 375 | function PATH:rmdir(P) 376 | assert_system(self) 377 | return fs.rmdir(self:fullpath(P)) 378 | end 379 | 380 | function PATH:rename(from, to, force) 381 | assert_system(self) 382 | from = self:fullpath(from) 383 | to = self:fullpath(to) 384 | return fs.move(from, to, force) 385 | end 386 | 387 | local each = require "path.findfile".load(function(opt) 388 | local has_dir_end = PATH:has_dir_end(opt.file) 389 | opt.file = PATH:fullpath(opt.file) 390 | if has_dir_end then opt.file = PATH:ensure_dir_end(opt.file) end 391 | return fs.each_impl(opt) 392 | end) 393 | 394 | function PATH:each(...) 395 | assert_system(self) 396 | return each(...) 397 | end 398 | 399 | local function copy_impl_batch(self, fs, src_dir, mask, dst_dir, opt) 400 | if not opt then opt = {} end 401 | 402 | local overwrite = opt.overwrite 403 | local accept = opt.accept 404 | local onerror = opt.error 405 | local chlen = #fs.DIR_SEP 406 | local count = 0 407 | 408 | local existed_dirs = {} 409 | local ok, err = fs.each_impl{file = src_dir .. fs.DIR_SEP .. mask, 410 | delay = opt.delay; recurse = opt.recurse; param = "pnm"; 411 | skipdirs = opt.skipdirs; skipfiles = opt.skipfiles; 412 | callback = function(path, name, mode) 413 | local rel = string.sub(path, #src_dir + chlen + 1) 414 | if #rel > 0 then rel = rel .. fs.DIR_SEP .. name else rel = name end 415 | local dst = dst_dir .. fs.DIR_SEP .. rel 416 | local src = path .. fs.DIR_SEP .. name 417 | 418 | if accept then 419 | local ok = accept(src, dst, opt) 420 | if not ok then return end 421 | end 422 | 423 | local ok, err = true 424 | if mode == "directory" then 425 | if not existed_dirs[dst] then 426 | if not fs.isdir(dst) then 427 | ok, err = self:mkdir(dst) 428 | end 429 | existed_dirs[dst] = true 430 | end 431 | else 432 | local dir = self:splitpath(dst) 433 | if not existed_dirs[dir] then 434 | if not fs.isdir(dst) then 435 | ok, err = self:mkdir(dir) 436 | end 437 | existed_dirs[dir] = true 438 | end 439 | if ok then 440 | ok, err = fs.copy(src, dst, not overwrite) 441 | end 442 | end 443 | 444 | if not ok and onerror then 445 | if not onerror(err, src, dst, opt) then -- break 446 | return true 447 | end 448 | else 449 | count = count + 1 450 | end 451 | end; 452 | } 453 | if ok or err then return ok, err end 454 | return count 455 | end 456 | 457 | local function remove_impl_batch(fs, src_dir, mask, opt) 458 | if not opt then opt = {} end 459 | 460 | local overwrite = opt.overwrite 461 | local accept = opt.accept 462 | local onerror = opt.error 463 | local chlen = #fs.DIR_SEP 464 | local count = 0 465 | local delay = (opt.delay == nil) and true or opt.delay 466 | 467 | local ok, err = fs.each_impl{file = src_dir .. fs.DIR_SEP .. mask, 468 | delay = delay; recurse = opt.recurse; reverse = true; param = "fm"; 469 | skipdirs = opt.skipdirs; skipfiles = opt.skipfiles; 470 | callback = function(src, mode) 471 | if accept then 472 | local ok = accept(src, opt) 473 | if not ok then return end 474 | end 475 | 476 | local ok, err 477 | if mode == "directory" then ok, err = fs.rmdir(src) 478 | else ok, err = fs.remove(src) end 479 | 480 | if not ok and onerror then 481 | if not onerror(err, src, opt) then -- break 482 | return true 483 | end 484 | else 485 | count = count + 1 486 | end 487 | end; 488 | } 489 | if ok or err then return ok, err end 490 | return count 491 | end 492 | 493 | function PATH:remove_impl(P) 494 | if self:isdir(P) then return fs.rmdir(P) end 495 | return fs.remove(P) 496 | end 497 | 498 | function PATH:copy(from, to, opt) 499 | from = self:fullpath(from) 500 | to = self:fullpath(to) 501 | 502 | if type(opt) == "boolean" then opt = {overwrite = opt} end 503 | 504 | local overwrite = opt and opt.overwrite 505 | local recurse = opt and opt.recurse 506 | 507 | local src_dir, src_name = self:splitpath(from) 508 | if recurse or src_name:find("[*?]") then -- batch mode 509 | return copy_impl_batch(self, fs, src_dir, src_name, to, opt) 510 | end 511 | if self.mkdir then self:mkdir(self:dirname(to)) end 512 | return fs.copy(from, to, not not overwrite) 513 | end 514 | 515 | function PATH:remove(P, opt) 516 | assert_system(self) 517 | local P = self:fullpath(P) 518 | local dir, name = self:splitpath(P) 519 | if (opt and opt.recurse) or name:find("[*?]") then -- batch mode 520 | return remove_impl_batch(fs, dir, name, opt) 521 | end 522 | return self:remove_impl(P) 523 | end 524 | 525 | end -- fs 526 | 527 | do -- Python aliases 528 | PATH.split = PATH.splitpath 529 | PATH.isabs = PATH.isfullpath 530 | PATH.normpath = PATH.normolize 531 | PATH.abspath = PATH.fullpath 532 | PATH.getctime = PATH.ctime 533 | PATH.getatime = PATH.atime 534 | PATH.getmtime = PATH.mtime 535 | PATH.getsize = PATH.size 536 | end 537 | 538 | local function path_new(o) 539 | o = o or {} 540 | for k, f in pairs(PATH) do 541 | if type(f) == 'function' then 542 | o[k] = function(...) 543 | if o == ... then return f(...) end 544 | return f(o, ...) 545 | end 546 | else 547 | o[k] = f 548 | end 549 | end 550 | return o 551 | end 552 | 553 | local function lock_table(t) 554 | return setmetatable(t,{ 555 | __newindex = function() 556 | error("Can not change path library", 2) 557 | end; 558 | __metatable = "lua-path object"; 559 | }) 560 | end 561 | 562 | local M = path_new(require "path.module") 563 | 564 | local path_cache = setmetatable({}, {__mode='v'}) 565 | 566 | function M.new(DIR_SEP) 567 | local is_win, sep 568 | 569 | if type(DIR_SEP) == 'string' then 570 | sep = DIR_SEP 571 | is_win = (DIR_SEP == '\\') 572 | elseif DIR_SEP ~= nil then 573 | assert(type(DIR_SEP) == 'boolean') 574 | is_win = DIR_SEP 575 | sep = is_win and '\\' or '/' 576 | else 577 | sep = M.DIR_SEP 578 | is_win = M.IS_WINDOWS 579 | end 580 | 581 | if M.DIR_SEP == sep then 582 | assert(M.IS_WINDOWS == is_win) 583 | return M 584 | end 585 | 586 | local o = path_cache[sep] 587 | 588 | if not o then 589 | o = path_new() 590 | o.DIR_SEP = sep 591 | o.IS_WINDOWS = is_win 592 | path_cache[sep] = lock_table(o) 593 | end 594 | 595 | return o 596 | end 597 | 598 | return lock_table(M) 599 | -------------------------------------------------------------------------------- /lua/path/win32/alien/fs.lua: -------------------------------------------------------------------------------- 1 | ------------------------------------------------------------------ 2 | -- 3 | -- Author: Alexey Melnichuk 4 | -- 5 | -- Copyright (C) 2013-2016 Alexey Melnichuk 6 | -- 7 | -- Licensed according to the included 'LICENCE' document 8 | -- 9 | -- This file is part of lua-path library. 10 | -- 11 | ------------------------------------------------------------------ 12 | 13 | local alien = require "alien" 14 | local autil = require "path.win32.alien.utils" 15 | local types = require "path.win32.alien.types" 16 | 17 | local CTYPES = types.CTYPES 18 | local CTYPE2LUA = types.CTYPE2LUA 19 | local DWORD = CTYPES.DWORD 20 | local HANDLE = CTYPES.HANDLE 21 | local FILETIME = CTYPES.FILETIME 22 | local BOOL = "uint" 23 | local INVALID_HANDLE = types.INVALID_HANDLE 24 | local LPVOID = "pointer" 25 | local LPDWORD = "ref " .. DWORD 26 | 27 | local NULL = nil 28 | local WIN32_FIND_DATAA = CTYPES.WIN32_FIND_DATAA 29 | local WIN32_FIND_DATAW = CTYPES.WIN32_FIND_DATAW 30 | 31 | local kernel32 = assert(alien.load("kernel32.dll")) 32 | local GetCurrentDirectoryA = assert(kernel32.GetCurrentDirectoryA) 33 | local GetCurrentDirectoryW = assert(kernel32.GetCurrentDirectoryW) 34 | local SetCurrentDirectoryA = assert(kernel32.SetCurrentDirectoryA) 35 | local SetCurrentDirectoryW = assert(kernel32.SetCurrentDirectoryW) 36 | local GetTempPathA = assert(kernel32.GetTempPathA) 37 | local GetTempPathW = assert(kernel32.GetTempPathW) 38 | local GetFileAttributesExA = assert(kernel32.GetFileAttributesExA) 39 | local GetFileAttributesExW = assert(kernel32.GetFileAttributesExW) 40 | local CopyFileA = assert(kernel32.CopyFileA) 41 | local CopyFileW = assert(kernel32.CopyFileW) 42 | local GetLastError = assert(kernel32.GetLastError) 43 | local FindFirstFileA = assert(kernel32.FindFirstFileA) 44 | local FindFirstFileW = assert(kernel32.FindFirstFileW) 45 | local FindNextFileA = assert(kernel32.FindNextFileA) 46 | local FindNextFileW = assert(kernel32.FindNextFileW) 47 | local FindClose_ = assert(kernel32.FindClose) 48 | local RemoveDirectoryA = assert(kernel32.RemoveDirectoryA) 49 | local RemoveDirectoryW = assert(kernel32.RemoveDirectoryW) 50 | local CreateDirectoryA = assert(kernel32.CreateDirectoryA) 51 | local CreateDirectoryW = assert(kernel32.CreateDirectoryW) 52 | local MoveFileExA = assert(kernel32.MoveFileExA) 53 | local MoveFileExW = assert(kernel32.MoveFileExW) 54 | local DeleteFileA = assert(kernel32.DeleteFileA) 55 | local DeleteFileW = assert(kernel32.DeleteFileW) 56 | local CreateFileA = assert(kernel32.CreateFileA) 57 | local CreateFileW = assert(kernel32.CreateFileW) 58 | local CloseHandle_ = assert(kernel32.CloseHandle) 59 | local SetFileTime_ = assert(kernel32.SetFileTime) 60 | local FormatMessageA = assert(kernel32.FormatMessageA) 61 | local FormatMessageW = assert(kernel32.FormatMessageW) 62 | local LocalFree = assert(kernel32.LocalFree) 63 | local DeviceIoControl_ = assert(kernel32.DeviceIoControl) 64 | 65 | GetCurrentDirectoryA:types{abi="stdcall", ret = DWORD, DWORD, LPVOID} 66 | GetCurrentDirectoryW:types{abi="stdcall", ret = DWORD, DWORD, LPVOID} 67 | 68 | SetCurrentDirectoryA:types{abi="stdcall", ret = BOOL, LPVOID} 69 | SetCurrentDirectoryW:types{abi="stdcall", ret = BOOL, LPVOID} 70 | 71 | CopyFileA:types{abi="stdcall", ret = BOOL, LPVOID, LPVOID, BOOL} 72 | CopyFileW:types{abi="stdcall", ret = BOOL, LPVOID, LPVOID, BOOL} 73 | 74 | GetTempPathA:types{abi="stdcall", ret = DWORD, DWORD, LPVOID} 75 | GetTempPathW:types{abi="stdcall", ret = DWORD, DWORD, LPVOID} 76 | 77 | GetFileAttributesExA:types{abi="stdcall", ret = DWORD, LPVOID, "int", LPVOID} 78 | GetFileAttributesExW:types{abi="stdcall", ret = DWORD, LPVOID, "int", LPVOID} 79 | 80 | FindFirstFileA:types{abi="stdcall", ret = HANDLE, LPVOID, LPVOID} 81 | FindFirstFileW:types{abi="stdcall", ret = HANDLE, LPVOID, LPVOID} 82 | 83 | FindNextFileA:types {abi="stdcall", ret = "int", HANDLE, LPVOID } 84 | FindNextFileW:types {abi="stdcall", ret = "int", HANDLE, LPVOID } 85 | 86 | FindClose_:types{abi="stdcall", ret = "int", HANDLE } 87 | 88 | RemoveDirectoryA:types{abi="stdcall", ret = BOOL, LPVOID} 89 | RemoveDirectoryW:types{abi="stdcall", ret = BOOL, LPVOID} 90 | 91 | CreateDirectoryA:types{abi="stdcall", ret = BOOL, LPVOID, LPVOID} 92 | CreateDirectoryW:types{abi="stdcall", ret = BOOL, LPVOID, LPVOID} 93 | 94 | DeleteFileA:types{abi="stdcall", ret = BOOL, LPVOID} 95 | DeleteFileW:types{abi="stdcall", ret = BOOL, LPVOID} 96 | 97 | MoveFileExA:types{abi="stdcall", ret = BOOL, LPVOID, LPVOID, DWORD} 98 | MoveFileExW:types{abi="stdcall", ret = BOOL, LPVOID, LPVOID, DWORD} 99 | 100 | CreateFileA:types {abi="stdcall", ret = HANDLE, LPVOID, DWORD, DWORD, LPVOID, DWORD, DWORD, LPVOID}; 101 | CreateFileW:types {abi="stdcall", ret = HANDLE, LPVOID, DWORD, DWORD, LPVOID, DWORD, DWORD, LPVOID}; 102 | 103 | CloseHandle_:types {abi="stdcall", ret = BOOL, HANDLE}; 104 | 105 | SetFileTime_:types {abi="stdcall", ret = BOOL, HANDLE, LPVOID, LPVOID, LPVOID}; 106 | 107 | GetLastError:types{abi="stdcall", ret = DWORD} 108 | 109 | FormatMessageW:types{abi="stdcall", ret = DWORD, DWORD, LPVOID, DWORD, DWORD, LPVOID, DWORD, LPVOID}; 110 | FormatMessageA:types{abi="stdcall", ret = DWORD, DWORD, LPVOID, DWORD, DWORD, LPVOID, DWORD, LPVOID}; 111 | 112 | LocalFree:types{abi="stdcall", ret = LPVOID, LPVOID}; 113 | 114 | DeviceIoControl_:types{abi="stdcall", ret = BOOL, HANDLE, DWORD, LPVOID, DWORD, LPVOID, DWORD, LPDWORD, LPVOID} 115 | 116 | local function GetCurrentDirectory(u) 117 | local n = (u and GetCurrentDirectoryW or GetCurrentDirectoryA)(0, NULL) 118 | if n == 0 then 119 | local err = GetLastError() 120 | return nil, err 121 | end 122 | local buf = alien.buffer(u and n*2 or n) 123 | n = (u and GetCurrentDirectoryW or GetCurrentDirectoryA)(n, buf) 124 | if n == 0 then 125 | local err = GetLastError() 126 | return nil, err 127 | end 128 | return buf:tostring(u and n*2 or n ) 129 | end 130 | 131 | local function SetCurrentDirectory(u, P) 132 | local ret 133 | if u then ret = SetCurrentDirectoryW(P .. "\0") 134 | else ret = SetCurrentDirectoryA(P) end 135 | if ret == 0 then 136 | local err = GetLastError() 137 | return nil, err 138 | end 139 | return true 140 | end 141 | 142 | local function GetTempPath(u) 143 | local n = (u and GetTempPathW or GetTempPathA)(0, NULL) 144 | if n == 0 then 145 | local err = GetLastError() 146 | return nil, err 147 | end 148 | local buf = alien.buffer(u and n*2 or n) 149 | n = (u and GetTempPathW or GetTempPathA)(n, buf) 150 | if n == 0 then 151 | local err = GetLastError() 152 | return nil, err 153 | end 154 | return buf:tostring(u and n*2 or n ) 155 | end 156 | 157 | local function CopyFile(u, src, dst, flag) 158 | local ret 159 | if u then ret = CopyFileW(src .. "\0", dst .. "\0", flag and 1 or 0) 160 | else ret = CopyFileA(src, dst, flag and 1 or 0) end 161 | if ret == 0 then 162 | local err = GetLastError() 163 | return nil, err 164 | end 165 | return true 166 | end 167 | 168 | local function GetFileAttributesEx(u, P) 169 | local ret, info 170 | if u then 171 | info = WIN32_FIND_DATAW:new() 172 | ret = GetFileAttributesExW(P .. "\0", 0, info()) 173 | else 174 | info = WIN32_FIND_DATAA:new() 175 | ret = GetFileAttributesExA(P, 0, info()) 176 | end 177 | if ret == 0 then 178 | local err = GetLastError() 179 | return nil, err 180 | end 181 | return info 182 | end 183 | 184 | local function FindClose(h) 185 | FindClose_(autil.gc_null(h)) 186 | end 187 | 188 | local function FindFirstFile(u, P) 189 | local ret, fd, err 190 | if u then 191 | fd = WIN32_FIND_DATAW:new() 192 | ret = FindFirstFileW(P .. "\0", fd()) 193 | else 194 | fd = WIN32_FIND_DATAA:new() 195 | ret = FindFirstFileA(P, fd()) 196 | end 197 | 198 | if ret == INVALID_HANDLE then 199 | local err = GetLastError() 200 | return nil, err 201 | end 202 | 203 | ret = autil.gc_wrap(ret, FindClose_) 204 | return ret, fd 205 | end 206 | 207 | local function FindNextFile(u, h, fd) 208 | local ret 209 | if u then ret = FindNextFileW(h.value, fd()) 210 | else ret = FindNextFileA(h.value, fd()) end 211 | return ret 212 | end 213 | 214 | local function RemoveDirectory(u, src) 215 | local ret 216 | if u then ret = RemoveDirectoryW(src .. "\0") 217 | else ret = RemoveDirectoryA(src) end 218 | if ret == 0 then 219 | local err = GetLastError() 220 | return nil, err 221 | end 222 | return true 223 | end 224 | 225 | local function CreateDirectory(u, src) 226 | local ret 227 | if u then ret = CreateDirectoryW(src .. "\0", NULL) 228 | else ret = CreateDirectoryA(src, NULL) end 229 | if ret == 0 then 230 | local err = GetLastError() 231 | return nil, err 232 | end 233 | return true 234 | end 235 | 236 | local function MoveFileEx(u, src, dst, flag) 237 | local ret 238 | if u then ret = MoveFileExW(src .. "\0", dst .. "\0", flag and flag or 0) 239 | else ret = MoveFileExA(src, dst, flag and flag or 0) end 240 | if ret == 0 then 241 | local err = GetLastError() 242 | return nil, err 243 | end 244 | return true 245 | end 246 | 247 | local function DeleteFile(u, src) 248 | local ret 249 | if u then ret = DeleteFileW(src .. "\0") 250 | else ret = DeleteFileA(src) end 251 | if ret == 0 then 252 | local err = GetLastError() 253 | return nil, err 254 | end 255 | return true 256 | end 257 | 258 | local function CloseHandle(h) 259 | return CloseHandle_(autil.gc_null(h)) 260 | end 261 | 262 | local function CreateFile(u, P, access, share, sec, mode, attr, template) 263 | local p = P 264 | if u then p = p .. "\0" end 265 | 266 | local h = (u and CreateFileW or CreateFileA)( 267 | p, access, share, sec or NULL, mode, attr, template or NULL 268 | ); 269 | 270 | if INVALID_HANDLE == h then 271 | local err = GetLastError() 272 | return nil, err 273 | end 274 | 275 | return autil.gc_wrap(h, CloseHandle_) 276 | end 277 | 278 | local function newft(t) 279 | if not t then return NULL end 280 | local v = FILETIME:new() 281 | v.dwLowDateTime, v.dwHighDateTime = t[1], t[2] 282 | return v 283 | end 284 | 285 | local function SetFileTime(h, c, a, m) 286 | local ctime, atime, mtime = newft(c), newft(a), newft(m) 287 | local ret = SetFileTime_(h.value, ctime and ctime(), atime and atime(), mtime and mtime()) 288 | if ret ~= 0 then return true end 289 | return nil, GetLastError() 290 | end 291 | 292 | local FORMAT_MESSAGE_ALLOCATE_BUFFER = 0x00000100 293 | local FORMAT_MESSAGE_IGNORE_INSERTS = 0x00000200 294 | local FORMAT_MESSAGE_FROM_STRING = 0x00000400 295 | local FORMAT_MESSAGE_FROM_HMODULE = 0x00000800 296 | local FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000 297 | local FORMAT_MESSAGE_ARGUMENT_ARRAY = 0x00002000 298 | local FORMAT_MESSAGE_MAX_WIDTH_MASK = 0x000000FF 299 | 300 | local function ErrorMessage(u, dwErr, lang) 301 | local lpMsgBuf = alien.array(LPVOID, 1) 302 | lang = lang or 0 303 | local ret = (u and FormatMessageW or FormatMessageA)( 304 | FORMAT_MESSAGE_ALLOCATE_BUFFER + FORMAT_MESSAGE_FROM_SYSTEM + FORMAT_MESSAGE_IGNORE_INSERTS, 305 | NULL, dwErr, lang, lpMsgBuf.buffer, 0, NULL 306 | ); 307 | 308 | if ret == 0 then 309 | local err = GetLastError() 310 | return "", err 311 | end 312 | 313 | local str = alien.tostring(lpMsgBuf[1], ret) 314 | ret = LocalFree(lpMsgBuf[1]); 315 | return str; 316 | end 317 | 318 | local function DeviceIoControl(h, code, inBuffer, inBufferSize, outBuffer, outBufferSize) 319 | if inBuffer == nil then inBuffer, inBufferSize = NULL, 0 end 320 | if outBuffer == nil then outBuffer, outBufferSize = NULL, 0 end 321 | local ret, dwTmp = DeviceIoControl_(h.value, code, 322 | inBuffer, inBufferSize, outBuffer, outBufferSize, 323 | 0, NULL 324 | ) 325 | if ret == 0 then 326 | local err = GetLastError() 327 | return nil, err 328 | end 329 | return ret, dwTmp 330 | end 331 | 332 | local FILE_FLAG_NO_BUFFERING = 0x20000000 333 | local FILE_ATTRIBUTE_NORMAL = 0x00000080 334 | local FILE_SHARE_READ = 0x00000001 335 | local FILE_SHARE_WRITE = 0x00000002 336 | local OPEN_EXISTING = 3 337 | local IOCTL_STORAGE_GET_DEVICE_NUMBER = 0x2D1080 338 | 339 | local function DiskNumber(u, P) 340 | local p 341 | if u then p = "\\\0\\\0.\0\\\0" .. P .. "\0" 342 | else p = "\\\\.\\" .. P end 343 | 344 | -- Open partition 345 | local hPart, err = CreateFile(u, p, 0, 346 | FILE_SHARE_READ + FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 347 | FILE_ATTRIBUTE_NORMAL + FILE_FLAG_NO_BUFFERING, NULL 348 | ); 349 | 350 | if not hPart then return nil, err end 351 | 352 | local Info = CTYPES.STORAGE_DEVICE_NUMBER:new(); 353 | local Info_size = CTYPES.STORAGE_DEVICE_NUMBER.size_ 354 | 355 | local ret, dwTmp = DeviceIoControl(hPart, IOCTL_STORAGE_GET_DEVICE_NUMBER, 356 | NULL, 0, Info(), Info_size 357 | ) 358 | if not ret then err = dwTmp end 359 | if dwTmp ~= Info_size then ret, err = nil, GetLastError() end 360 | 361 | CloseHandle(hPart) 362 | if not ret then return nil, err end 363 | return { 364 | Info.DeviceType, 365 | Info.DeviceNumber, 366 | Info.PartitionNumber, 367 | } 368 | end 369 | 370 | return { 371 | A = { 372 | GetCurrentDirectory = function(...) return GetCurrentDirectory(false, ...) end; 373 | SetCurrentDirectory = function(...) return SetCurrentDirectory(false, ...) end; 374 | GetTempPath = function(...) return GetTempPath (false, ...) end; 375 | GetFileAttributesEx = function(...) return GetFileAttributesEx(false, ...) end; 376 | CopyFile = function(...) return CopyFile (false, ...) end; 377 | FindFirstFile = function(...) return FindFirstFile (false, ...) end; 378 | FindNextFile = function(...) return FindNextFile (false, ...) end; 379 | RemoveDirectory = function(...) return RemoveDirectory (false, ...) end; 380 | DeleteFile = function(...) return DeleteFile (false, ...) end; 381 | CreateDirectory = function(...) return CreateDirectory (false, ...) end; 382 | CreateFile = function(...) return CreateFile (false, ...) end; 383 | MoveFileEx = function(...) return MoveFileEx (false, ...) end; 384 | ErrorMessage = function(...) return ErrorMessage (false, ...) end; 385 | DiskNumber = function(...) return DiskNumber (false, ...) end; 386 | FindClose = FindClose; 387 | CloseHandle = CloseHandle; 388 | SetFileTime = SetFileTime; 389 | DeviceIoControl = DeviceIoControl; 390 | WIN32_FIND_DATA2TABLE = CTYPE2LUA.WIN32_FIND_DATAA; 391 | DIR_SEP = "\\"; 392 | ANY_MASK = "*"; 393 | }; 394 | W = { 395 | GetCurrentDirectory = function(...) return GetCurrentDirectory(true, ...) end; 396 | SetCurrentDirectory = function(...) return SetCurrentDirectory(true, ...) end; 397 | GetTempPath = function(...) return GetTempPath (true, ...) end; 398 | GetFileAttributesEx = function(...) return GetFileAttributesEx(true, ...) end; 399 | CopyFile = function(...) return CopyFile (true, ...) end; 400 | FindFirstFile = function(...) return FindFirstFile (true, ...) end; 401 | FindNextFile = function(...) return FindNextFile (true, ...) end; 402 | RemoveDirectory = function(...) return RemoveDirectory (true, ...) end; 403 | DeleteFile = function(...) return DeleteFile (true, ...) end; 404 | CreateDirectory = function(...) return CreateDirectory (true, ...) end; 405 | CreateFile = function(...) return CreateFile (true, ...) end; 406 | MoveFileEx = function(...) return MoveFileEx (true, ...) end; 407 | ErrorMessage = function(...) return ErrorMessage (true, ...) end; 408 | DiskNumber = function(...) return DiskNumber (true, ...) end; 409 | FindClose = FindClose; 410 | CloseHandle = CloseHandle; 411 | SetFileTime = SetFileTime; 412 | DeviceIoControl = DeviceIoControl; 413 | WIN32_FIND_DATA2TABLE = CTYPE2LUA.WIN32_FIND_DATAW; 414 | DIR_SEP = "\\\000"; 415 | ANY_MASK = "*\000"; 416 | }; 417 | } -------------------------------------------------------------------------------- /test/test.lua: -------------------------------------------------------------------------------- 1 | local lunit = require "lunit" 2 | local TEST_CASE = lunit.TEST_CASE 3 | 4 | local path = require "path" 5 | 6 | local path_win = path.new('\\') 7 | local path_unx = path.new('/') 8 | 9 | local function mkfile(P, data) 10 | P = path.fullpath(P) 11 | path.mkdir(path.dirname(P)) 12 | local f, e = io.open(P, "w+b") 13 | if not f then return nil, err end 14 | if data then assert(f:write(data)) end 15 | f:close() 16 | return P 17 | end 18 | 19 | local function read_file(P) 20 | local f, err = io.open(P, "rb") 21 | if not f then return nil, err end 22 | local data, err = f:read("*all") 23 | f:close() 24 | if data then return data end 25 | return nil, err 26 | end 27 | 28 | local function up(str) 29 | return path.IS_WINDOWS and str:upper() or str 30 | end 31 | 32 | local function clone(t, o) 33 | o = o or {} 34 | for k,v in pairs(t) do 35 | o[ k ] = v 36 | end 37 | return o 38 | end 39 | 40 | local _ENV = TEST_CASE('PATH manipulation') if true then 41 | 42 | local function testpath(pth,p1,p2,p3) 43 | local dir,rest = path.splitpath(pth) 44 | local name,ext = path.splitext(rest) 45 | assert_equal(p1, dir ) 46 | assert_equal(p2, name) 47 | assert_equal(p3, ext ) 48 | end 49 | 50 | function test_penlight_1() 51 | testpath ([[/bonzo/dog_stuff/cat.txt]],[[/bonzo/dog_stuff]],'cat','.txt') 52 | testpath ([[/bonzo/dog/cat/fred.stuff]],'/bonzo/dog/cat','fred','.stuff') 53 | testpath ([[../../alice/jones]],'../../alice','jones','') 54 | testpath ([[alice]],'','alice','') 55 | testpath ([[/path-to/dog/]],[[/path-to/dog]],'','') 56 | end 57 | 58 | function test_penlight_2() 59 | local p = path_unx:normalize( '/a/b' ) 60 | assert_equal('/a/b',p) 61 | assert_equal(p, path_unx:normalize( '/a/fred/../b' )) 62 | assert_equal(p, path_unx:normalize( '/a//b' )) 63 | assert_equal(p, path_unx:normalize( '/a/./b' )) 64 | 65 | local p = path_win:normalize( '/a/b' ) 66 | assert_equal('\\a\\b',p) 67 | assert_equal(p, path_win:normalize( '/a/fred/../b' )) 68 | assert_equal(p, path_win:normalize( '/a//b' )) 69 | assert_equal(p, path_win:normalize( '/a/./b' )) 70 | end 71 | 72 | function test_penlight_3() 73 | if not path.isdir then assert_false('lfs module not found') end 74 | assert ( path.isdir( "../lua" )) 75 | assert_false ( path.isfile( "../lua" )) 76 | 77 | assert ( path.isfile( "../lua/path.lua" ) ) 78 | assert_false( path.isdir( "../lua/path.lua" )) 79 | end 80 | 81 | function test_system() 82 | if not path.isdir then assert_false('lfs module not found') end 83 | if path.IS_WINDOWS then 84 | assert_error(function()path_unx:isdir("/any/") end) 85 | assert_pass (function()path_win:isdir("c:\\") end) 86 | else 87 | assert_pass (function()path_unx:isdir("/any/") end) 88 | assert_error(function()path_win:isdir("c:\\") end) 89 | end 90 | end 91 | 92 | function test_split() 93 | assert_equal('a', path_unx:root('/a/b/c')) 94 | assert_equal('', path_unx:root('a/b/c')) 95 | 96 | assert_equal('host', path_win:root('\\\\host\\a\\b\\c')) 97 | assert_equal('a:', path_win:root('a:\\b\\c')) 98 | assert_equal('', path_win:root('\\b\\c')) 99 | end 100 | 101 | function test_splitext() 102 | testpath ('.log','','.log','') 103 | testpath ('path/.log','path','.log','') 104 | testpath ('log','','log','') 105 | testpath ('.log/','.log','','') 106 | testpath ('.1001.log','','.1001','.log') 107 | 108 | local root, ext = path.splitext(".log") 109 | assert_equal(".log", root) 110 | assert_equal("", ext) 111 | assert_equal(ext, path.extension(".log")) 112 | 113 | root, ext = path.splitext("test/.log") 114 | assert_equal("test/.log", root) 115 | assert_equal("", ext) 116 | assert_equal(ext, path.extension("test/.log")) 117 | 118 | root, ext = path.splitext("test/1.log") 119 | assert_equal("test/1", root) 120 | assert_equal(".log", ext) 121 | assert_equal(ext, path.extension("test/1.log")) 122 | 123 | root, ext = path.splitext("test/.1.log") 124 | assert_equal("test/.1", root) 125 | assert_equal(".log", ext) 126 | assert_equal(ext, path.extension("test/.1.log")) 127 | end 128 | 129 | function test_splitdrive() 130 | local a, b 131 | a,b = path_unx:splitdrive('/root/etc') 132 | assert_equal('', a) assert_equal('/root/etc', b) 133 | 134 | a,b = path_win:splitdrive('c:\\root\\etc') 135 | assert_equal('c:', a) assert_equal('root\\etc', b) 136 | end 137 | 138 | function test_norm() 139 | assert_equal("..\\hello", path_win:normalize("..\\hello")) 140 | assert_equal("..\\hello", path_win:normalize("..\\hello\\world\\..")) 141 | assert_equal("c:\\hello", path_win:normalize("c:\\..\\hello")) 142 | assert_equal("c:\\hello", path_win:normalize("c:\\hello\\.")) 143 | assert_equal("c:\\hello", path_win:normalize("c:\\hello\\.\\.")) 144 | assert_equal("\\hello", path_win:normalize("\\..\\hello")) -- full path without drive 145 | assert_equal("\\\\host\\hello", path_win:normalize("\\\\host\\..\\hello")) 146 | 147 | assert_equal("/hello", path_unx:normalize("\\c\\..\\hello")) 148 | assert_equal("../hello", path_unx:normalize("..\\hello\\world\\..")) 149 | assert_equal("/home/test", path_unx:normalize("/home/test/.")) 150 | assert_equal("/home/test", path_unx:normalize("/home/test/./.")) 151 | assert_equal("/home/test/world",path_unx:normalize("/home/test/./world")) 152 | assert_equal("/home/test", path_unx:normalize("\\home\\test\\.")) 153 | assert_equal("/", path_unx:normalize("/")) 154 | assert_equal("/", path_unx:normalize("/.")) 155 | assert_equal("/", path_unx:normalize("/./.")) 156 | assert_equal("/", path_unx:normalize("/./")) 157 | assert_equal(".", path_unx:normalize("././")) 158 | assert_equal("/dev", path_unx:normalize("/./dev")) 159 | end 160 | 161 | function test_quote() 162 | assert_equal('c:\\hello', path_win:quote('c:\\hello')) 163 | assert_equal('"c:\\hello world"', path_win:quote('c:\\hello world')) 164 | assert_equal('/hello', path_unx:quote('/hello')) 165 | assert_equal('"/hello world"', path_unx:quote('/hello world')) 166 | 167 | assert_equal('c:\\hello', path_win:unquote('c:\\hello')) 168 | assert_equal('c:\\hello', path_win:unquote('"c:\\hello"')) 169 | assert_equal('c:\\"hello"', path_win:unquote('c:\\"hello"')) 170 | assert_equal('c:\\hello world', path_win:unquote('"c:\\hello world"')) 171 | assert_equal('c:\\hello world', path_win:unquote('c:\\hello world')) 172 | assert_equal('/hello', path_unx:unquote('/hello')) 173 | assert_equal('/hello', path_unx:unquote('"/hello"')) 174 | assert_equal('/"hello"', path_unx:unquote('/"hello"')) 175 | assert_equal('/hello world', path_unx:unquote('/hello world')) 176 | assert_equal('/hello world', path_unx:unquote('"/hello world"')) 177 | end 178 | 179 | function test_dir_end() 180 | assert_equal('c:', path_win:remove_dir_end('c:\\')) 181 | assert_equal('c:', path_win:remove_dir_end('c:\\\\')) 182 | assert_equal('c:\\.', path_win:remove_dir_end('c:\\.\\')) 183 | assert_equal('c:\\', path_win:ensure_dir_end('c:')) 184 | 185 | assert_equal('', path_unx:remove_dir_end('/')) 186 | assert_equal('', path_unx:remove_dir_end('//')) 187 | assert_equal('.', path_unx:remove_dir_end('./')) 188 | assert_equal('/', path_unx:ensure_dir_end('')) 189 | assert_equal('/', path_unx:ensure_dir_end('/')) 190 | end 191 | 192 | function test_join() 193 | assert_equal("hello", path_win:join("hello")) 194 | assert_equal("hello\\world", path_win:join("hello", "", "world")) 195 | assert_equal("c:\\world\\some\\path", path_win:join("hello", "", "c:\\world", "some", "path")) 196 | assert_equal("hello\\", path_win:join("hello", "")) 197 | end 198 | 199 | function test_dot_notation() 200 | assert_equal("hello\\world", path_win.join("hello", "", "world")) 201 | assert_equal("hello/world", path_unx.join("hello", "", "world")) 202 | assert_equal('/', path_unx.ensure_dir_end('/')) 203 | assert_equal('host', path_win.root('\\\\host\\a\\b\\c')) 204 | end 205 | 206 | end 207 | 208 | local _ENV = TEST_CASE('PATH system error') if true then 209 | 210 | function test() 211 | local p = path.IS_WINDOWS and path_unx or path_win 212 | assert_boolean(path.IS_WINDOWS) 213 | assert_boolean(p.IS_WINDOWS) 214 | assert_not_equal(path.IS_WINDOWS, p.IS_WINDOWS) 215 | assert_error(function() p:mkdir('./1') end) 216 | assert_error(function() p:size('./1.txt') end) 217 | end 218 | 219 | end 220 | 221 | local _ENV = TEST_CASE('PATH fullpath') if true then 222 | 223 | function test_user_home() 224 | local p = assert_string(path.user_home()) 225 | assert_equal(p, path.isdir(p)) 226 | assert_equal(p, path.fullpath("~")) 227 | end 228 | 229 | function test_win() 230 | if path.IS_WINDOWS then 231 | local p = assert_string(path.currentdir()) 232 | assert_equal(p, path.isdir(p)) 233 | local _, tp = path.splitroot(p) 234 | assert_equal(p, path.fullpath(path.DIR_SEP .. tp)) 235 | end 236 | end 237 | 238 | end 239 | 240 | local _ENV = TEST_CASE('PATH make dir') if true then 241 | 242 | local cwd 243 | 244 | function teardown() 245 | path.remove(path.join(cwd, '1', '2', '3', 'test.dat')) 246 | path.rmdir(path.join(cwd, '1', '2', '3')) 247 | path.rmdir(path.join(cwd, '1', '2')) 248 | path.rmdir(path.join(cwd, '1')) 249 | end 250 | 251 | function setup() 252 | cwd = assert_string(path.currentdir()) 253 | teardown() 254 | end 255 | 256 | function test_mkdir_nested() 257 | local DST = path.join(cwd, '1', '2', '3') 258 | assert_equal(cwd, path.isdir(cwd)) 259 | assert_string(path.mkdir(DST)) 260 | assert_true (path.rmdir(DST)) 261 | end 262 | 263 | function test_mkdir() 264 | local DST = path.join(cwd, '1') 265 | assert_equal(cwd, path.isdir(cwd)) 266 | assert_string(path.mkdir(DST)) 267 | assert_true (path.rmdir(DST)) 268 | end 269 | 270 | function test_clean() 271 | assert(path.isdir(cwd)) 272 | assert(path.mkdir(path.join(cwd, '1', '2', '3'))) 273 | assert_nil(path.rmdir(path.join(cwd, '1'))) 274 | assert(mkfile(path.join(cwd, '1', '2', '3', 'test.dat'))) 275 | assert_nil(path.rmdir(path.join(cwd, '1', '2', '3'))) 276 | assert(path.remove(path.join(cwd, '1', '2', '3', 'test.dat'))) 277 | assert(path.remove(path.join(cwd, '1', '2', '3'))) 278 | assert_false( path.exists(path.join(cwd, '1', '2', '3')) ) 279 | end 280 | 281 | end 282 | 283 | local _ENV = TEST_CASE('PATH findfile') if true then 284 | 285 | local cwd, files, dirs 286 | 287 | function teardown() 288 | collectgarbage("collect") -- force clean lfs.dir 289 | collectgarbage("collect") 290 | path.remove(path.join(cwd, '1', '2', '3', 'test.dat')) 291 | path.remove(path.join(cwd, '1', '2', '3', 'test.txt')) 292 | path.remove(path.join(cwd, '1', '2', '3', 'file.dat')) 293 | path.rmdir(path.join(cwd, '1', '2', '3')) 294 | path.rmdir(path.join(cwd, '1', '2')) 295 | path.rmdir(path.join(cwd, '1')) 296 | end 297 | 298 | function setup() 299 | cwd = assert_string(path.currentdir()) 300 | teardown() 301 | path.mkdir(path.join(cwd, '1', '2', '3')) 302 | mkfile(path.join(cwd, '1', '2', '3', 'test.dat'), '12345') 303 | mkfile(path.join(cwd, '1', '2', '3', 'test.txt'), '12345') 304 | mkfile(path.join(cwd, '1', '2', '3', 'file.dat'), '12345') 305 | 306 | files = { 307 | [ up(path.join(cwd, '1', '2', '3', 'test.dat')) ] = true; 308 | [ up(path.join(cwd, '1', '2', '3', 'test.txt')) ] = true; 309 | [ up(path.join(cwd, '1', '2', '3', 'file.dat')) ] = true; 310 | } 311 | 312 | dirs = { 313 | [ up(path.join(cwd, '1', '2', '3')) ] = true; 314 | [ up(path.join(cwd, '1', '2')) ] = true; 315 | [ up(path.join(cwd, '1' )) ] = true; 316 | } 317 | end 318 | 319 | function test_cwd() 320 | assert_equal(cwd, path.fullpath(".")) 321 | end 322 | 323 | function test_attr() 324 | for P in pairs(files)do assert(path.exists(P)) end 325 | for P in pairs(files)do assert(path.isfile(P)) end 326 | for P in pairs(files)do assert_equal(5, path.size(P)) end 327 | 328 | local ts = os.time() 329 | path.each("./1/*", function(f) 330 | assert(path.isfile(f)) 331 | assert(path.touch(f, ts)) 332 | end, {skipdirs=true, recurse=true}) 333 | 334 | path.each("./1/*", "ft", function(f,mt) 335 | if math.abs(ts - mt) > 1 then 336 | assert_equal(ts, mt) 337 | end 338 | end, {skipdirs=true, recurse=true}) 339 | end 340 | 341 | function test_findfile() 342 | local params 343 | 344 | params = clone(files) 345 | path.each("./1/2/3/*.*", function(f) 346 | f = up(f) 347 | assert_not_nil(params[f], "unexpected: " .. f) 348 | params[f] = nil 349 | end) 350 | assert_nil(next(params)) 351 | 352 | params = clone(files) 353 | for f in path.each("./1/2/3/*.*") do 354 | f = up(f) 355 | assert_not_nil(params[f], "unexpected: " .. f) 356 | params[f] = nil 357 | end 358 | assert_nil(next(params)) 359 | 360 | params = clone(files) 361 | params = clone(dirs,params) 362 | path.each("./1/*", function(f) 363 | f = up(f) 364 | assert_not_nil(params[f], "unexpected: " .. f) 365 | params[f] = nil 366 | end, {recurse=true}) 367 | assert_equal(up(path.join(cwd, '1' )), next(params)) 368 | assert_nil(next(params, up(path.join(cwd, '1' )))) 369 | 370 | params = clone(files) 371 | path.each("./1/2/3/*.*", "fz", function(f, sz) 372 | f = up(f) 373 | assert_not_nil(params[f], "unexpected: " .. f) 374 | assert_equal(5, sz) 375 | params[f] = nil 376 | end) 377 | assert_nil(next(params)) 378 | 379 | params = clone(files) 380 | for f, sz in path.each("./1/2/3/*.*", "fz") do 381 | f = up(f) 382 | assert_not_nil(params[f], "unexpected: " .. f) 383 | assert_equal(5, sz) 384 | params[f] = nil 385 | end 386 | assert_nil(next(params)) 387 | 388 | params = clone(dirs) 389 | path.each("./*", "fzm", function(f, sz, m) 390 | f = up(f) 391 | assert_not_nil(params[f], "unexpected: " .. f) 392 | assert_equal('directory', m) 393 | if path.IS_WINDOWS then assert_equal(0, sz) end 394 | params[f] = nil 395 | end, {skipfiles=true, recurse=true}) 396 | assert_nil(next(params)) 397 | 398 | end 399 | 400 | function test_findfile_mask() 401 | params = clone(files) 402 | path.each("./1/2/3/t*.*", function(f) 403 | f = up(f) 404 | assert_not_nil(params[f], "unexpected: " .. f) 405 | params[f] = nil 406 | end) 407 | assert_not_nil(next(params)) 408 | end 409 | 410 | function test_findfile_break() 411 | local flag = false 412 | path.each("./1/2/3/*.*", function() 413 | assert_false(flag) 414 | flag = true 415 | return 'break' 416 | end) 417 | assert_true(flag) 418 | end 419 | 420 | end 421 | 422 | local _ENV = TEST_CASE('PATH rename') if true then 423 | 424 | local cwd 425 | 426 | function teardown() 427 | path.remove(path.join(cwd, '1', 'from.dat')) 428 | path.remove(path.join(cwd, '1', 'to.dat' )) 429 | path.remove(path.join(cwd, '1', 'to.txt')) 430 | path.remove(path.join(cwd, '1', 'to')) 431 | path.remove(path.join(cwd, '1')) 432 | end 433 | 434 | function setup() 435 | cwd = assert_string(path.currentdir()) 436 | teardown() 437 | path.mkdir(path.join(cwd, '1')) 438 | path.mkdir(path.join(cwd, '1', 'to')) 439 | mkfile(path.join(cwd, '1', 'from.dat')) 440 | mkfile(path.join(cwd, '1', 'to.dat' )) 441 | 442 | assert(path.isfile(path.join(cwd, '1', 'from.dat'))) 443 | assert(path.isfile(path.join(cwd, '1', 'to.dat' ))) 444 | assert(path.isdir (path.join(cwd, '1', 'to' ))) 445 | end 446 | 447 | function test_rename_fail() 448 | assert_nil( path.rename( 449 | path.join(cwd, '1', 'from.dat'), 450 | path.join(cwd, '1', 'to.dat') 451 | )) 452 | assert(path.exists(path.join(cwd, '1', 'from.dat'))) 453 | assert(path.exists(path.join(cwd, '1', 'to.dat'))) 454 | 455 | assert_nil( path.rename( 456 | path.join(cwd, '1', 'from.dat'), 457 | path.join(cwd, '1', 'to') 458 | )) 459 | assert(path.exists(path.join(cwd, '1', 'from.dat'))) 460 | assert(path.exists(path.join(cwd, '1', 'to'))) 461 | 462 | assert_nil( path.rename( 463 | path.join(cwd, '1', 'from.txt'), 464 | path.join(cwd, '1', 'to'), 465 | true 466 | )) 467 | assert(path.exists(path.join(cwd, '1', 'from.dat'))) 468 | assert(path.exists(path.join(cwd, '1', 'to'))) 469 | end 470 | 471 | function test_rename_pass1() 472 | assert( path.rename( 473 | path.join(cwd, '1', 'from.dat'), 474 | path.join(cwd, '1', 'to.txt') 475 | )) 476 | assert_false(path.exists(path.join(cwd, '1', 'from.dat'))) 477 | assert(path.exists(path.join(cwd, '1', 'to.dat'))) 478 | end 479 | 480 | function test_rename_force_file() 481 | assert( path.rename( 482 | path.join(cwd, '1', 'from.dat'), 483 | path.join(cwd, '1', 'to.dat'), 484 | true 485 | )) 486 | assert_false(path.exists(path.join(cwd, '1', 'from.dat'))) 487 | assert(path.exists(path.join(cwd, '1', 'to.dat'))) 488 | end 489 | 490 | function test_rename_force_dir() 491 | assert_nil( path.rename( 492 | path.join(cwd, '1', 'from.dat'), 493 | path.join(cwd, '1', 'to'), 494 | true 495 | )) 496 | assert_equal(path.join(cwd, '1', 'from.dat'), path.exists(path.join(cwd, '1', 'from.dat'))) 497 | assert_equal(path.join(cwd, '1', 'to'), path.isdir(path.join(cwd, '1', 'to'))) 498 | end 499 | 500 | end 501 | 502 | local _ENV = TEST_CASE('PATH chdir') if true then 503 | 504 | local cwd 505 | 506 | function teardown() 507 | if cwd then path.chdir(cwd) end 508 | path.rmdir(path.join(cwd, '1', '2')) 509 | path.rmdir(path.join(cwd, '1')) 510 | end 511 | 512 | function setup() 513 | cwd = path.currentdir() 514 | path.mkdir(path.join(cwd, '1')) 515 | path.mkdir(path.join(cwd, '1', '2')) 516 | end 517 | 518 | function test_chdir() 519 | assert(path.isdir('./1')) 520 | assert_false(path.exists('./2')) 521 | assert_true(path.chdir('./1')) 522 | assert_false(path.exists('./1')) 523 | assert(path.isdir('./2')) 524 | end 525 | 526 | end 527 | 528 | local _ENV = TEST_CASE('PATH copy') if true then 529 | 530 | local cwd, files 531 | 532 | function teardown() 533 | collectgarbage("collect") -- force clean lfs.dir 534 | collectgarbage("collect") 535 | path.remove(path.join(cwd, '1', 'a1.txt')) 536 | path.remove(path.join(cwd, '1', 'a2.txt')) 537 | path.remove(path.join(cwd, '1', 'b1.txt')) 538 | path.remove(path.join(cwd, '1', 'b2.txt')) 539 | path.remove(path.join(cwd, '1', '2', '3', 'a1.txt')) 540 | path.remove(path.join(cwd, '1', '2', '3', 'a2.txt')) 541 | path.remove(path.join(cwd, '1', '2', '3', 'b1.txt')) 542 | path.remove(path.join(cwd, '1', '2', '3', 'b2.txt')) 543 | path.remove(path.join(cwd, '1', '2', 'a1.txt')) 544 | path.remove(path.join(cwd, '1', '2', 'a2.txt')) 545 | path.remove(path.join(cwd, '1', '2', 'b1.txt')) 546 | path.remove(path.join(cwd, '1', '2', 'b2.txt')) 547 | path.remove(path.join(cwd, '1', '2', '3')) 548 | path.remove(path.join(cwd, '1', '2')) 549 | path.remove(path.join(cwd, '1')) 550 | 551 | path.remove(path.join(cwd, '2', 'a1.txt')) 552 | path.remove(path.join(cwd, '2', 'a2.txt')) 553 | path.remove(path.join(cwd, '2', 'b1.txt')) 554 | path.remove(path.join(cwd, '2', 'b2.txt')) 555 | path.remove(path.join(cwd, '2', '2', 'a1.txt')) 556 | path.remove(path.join(cwd, '2', '2', 'a2.txt')) 557 | path.remove(path.join(cwd, '2', '2', 'b1.txt')) 558 | path.remove(path.join(cwd, '2', '2', 'b2.txt')) 559 | path.remove(path.join(cwd, '2', '2', '3', 'a1.txt')) 560 | path.remove(path.join(cwd, '2', '2', '3', 'a2.txt')) 561 | path.remove(path.join(cwd, '2', '2', '3', 'b1.txt')) 562 | path.remove(path.join(cwd, '2', '2', '3', 'b2.txt')) 563 | 564 | path.remove(path.join(cwd, '2', '2', '3')) 565 | path.remove(path.join(cwd, '2', '2' )) 566 | path.remove(path.join(cwd, '2', 'to')) 567 | path.remove(path.join(cwd, '2')) 568 | end 569 | 570 | function setup() 571 | cwd = assert_string(path.currentdir()) 572 | teardown() 573 | 574 | path.mkdir(path.join(cwd, '1')) 575 | path.mkdir(path.join(cwd, '2', 'to')) 576 | mkfile(path.join(cwd, '1', 'a1.txt'), '12345') 577 | mkfile(path.join(cwd, '1', 'a2.txt'), '54321') 578 | mkfile(path.join(cwd, '1', 'b1.txt'), '12345') 579 | mkfile(path.join(cwd, '1', 'b2.txt'), '54321') 580 | 581 | files = { 582 | [path.join(cwd, '1', 'a1.txt'):upper()] = true; 583 | [path.join(cwd, '1', 'a2.txt'):upper()] = true; 584 | [path.join(cwd, '1', 'b1.txt'):upper()] = true; 585 | [path.join(cwd, '1', 'b2.txt'):upper()] = true; 586 | } 587 | end 588 | 589 | function test_copy_fail() 590 | assert_nil( path.copy( 591 | path.join(cwd, '1', 'a1.txt'), 592 | path.join(cwd, '1', 'a2.txt') 593 | )) 594 | assert_equal("54321", read_file(path.join(cwd, '1', 'a2.txt'))) 595 | end 596 | 597 | function test_copy_fail_bool() 598 | assert_nil( path.copy( 599 | path.join(cwd, '1', 'a1.txt'), 600 | path.join(cwd, '1', 'a2.txt'), 601 | false 602 | )) 603 | assert_equal("54321", read_file(path.join(cwd, '1', 'a2.txt'))) 604 | end 605 | 606 | function test_copy_overwrite() 607 | assert( path.copy( 608 | path.join(cwd, '1', 'a1.txt'), 609 | path.join(cwd, '1', 'a2.txt'), 610 | {overwrite = true} 611 | )) 612 | assert_equal("12345", read_file(path.join(cwd, '1', 'a2.txt'))) 613 | end 614 | 615 | function test_copy_overwrite_dir() 616 | assert_nil( path.copy( 617 | path.join(cwd, '1', 'a1.txt'), 618 | path.join(cwd, '2', 'to'), 619 | {overwrite = true} 620 | )) 621 | assert_equal(path.join(cwd, '2', 'to'), path.isdir(path.join(cwd, '2', 'to'))) 622 | end 623 | 624 | function test_copy_overwrite_bool() 625 | assert( path.copy( 626 | path.join(cwd, '1', 'a1.txt'), 627 | path.join(cwd, '1', 'a2.txt'), 628 | true 629 | )) 630 | assert_equal("12345", read_file(path.join(cwd, '1', 'a2.txt'))) 631 | end 632 | 633 | function test_copy_mkdir() 634 | assert( path.copy( 635 | path.join(cwd, '1', 'a1.txt'), 636 | path.join(cwd, '1', '2', '3', 'a2.txt') 637 | )) 638 | assert_equal("12345", read_file(path.join(cwd, '1', '2', '3', 'a2.txt'))) 639 | end 640 | 641 | function test_copy_batch() 642 | assert(path.copy( 643 | path.join(cwd, '1', 'a*.txt'), 644 | path.join(cwd, '1', '2') 645 | )) 646 | assert_equal("12345", read_file(path.join(cwd, '1', '2', 'a1.txt'))) 647 | assert_equal("54321", read_file(path.join(cwd, '1', '2', 'a2.txt'))) 648 | assert_true(path.remove(path.join(cwd, '1', '2', 'a1.txt'))) 649 | assert_true(path.remove(path.join(cwd, '1', '2', 'a2.txt'))) 650 | 651 | local fname 652 | path.each(path.join(cwd, '1', '2', '*'), function(f) 653 | fname = f 654 | return true 655 | end) 656 | assert_nil(fname) 657 | end 658 | 659 | function test_copy_batch_recurse() 660 | path.mkdir(path.join(cwd, '1')) 661 | path.mkdir(path.join(cwd, '1', '2')) 662 | path.mkdir(path.join(cwd, '1', '2', '3')) 663 | 664 | mkfile(path.join(cwd, '1', 'a1.txt'), '12345') 665 | mkfile(path.join(cwd, '1', 'a2.txt'), '54321') 666 | mkfile(path.join(cwd, '1', 'b1.txt'), '12345') 667 | mkfile(path.join(cwd, '1', 'b2.txt'), '54321') 668 | 669 | mkfile(path.join(cwd, '1', '2', '3', 'a1.txt'), '12345') 670 | mkfile(path.join(cwd, '1', '2', '3', 'a2.txt'), '54321') 671 | mkfile(path.join(cwd, '1', '2', '3', 'b1.txt'), '12345') 672 | mkfile(path.join(cwd, '1', '2', '3', 'b2.txt'), '54321') 673 | 674 | assert(path.copy( 675 | path.join(cwd, '1', 'a*.txt'), 676 | path.join(cwd, '2'), 677 | {recurse = true} 678 | )) 679 | 680 | assert_equal("12345", read_file(path.join(cwd, '2', 'a1.txt'))) 681 | assert_equal("54321", read_file(path.join(cwd, '2', 'a2.txt'))) 682 | assert_equal("12345", read_file(path.join(cwd, '2', '2', '3', 'a1.txt'))) 683 | assert_equal("54321", read_file(path.join(cwd, '2', '2', '3', 'a2.txt'))) 684 | end 685 | 686 | function test_copy_batch_dir() 687 | path.mkdir(path.join(cwd, '1')) 688 | path.mkdir(path.join(cwd, '1', '2')) 689 | path.mkdir(path.join(cwd, '1', '2', '3')) 690 | 691 | assert(path.copy( 692 | path.join(cwd, '1', '3*'), 693 | path.join(cwd, '2'), 694 | {recurse = true,skipdirs=false} 695 | )) 696 | 697 | assert_equal(path.join(cwd, '2', '2', '3'), path.isdir(path.join(cwd, '2', '2', '3'))) 698 | end 699 | 700 | function test_copy_accept() 701 | local options options = { 702 | skipdirs = true; 703 | accept = function(src, des, opt) 704 | local key = src:upper() 705 | assert_true(files[key]) 706 | assert_equal(options, opt) 707 | files[key] = nil; 708 | return not path.basename(src):find("^b") 709 | end; 710 | } 711 | assert(path.copy( 712 | path.join(cwd, '1', '*'), 713 | path.join(cwd, '1', '2'), 714 | options 715 | )) 716 | assert_nil(next(files)) 717 | 718 | assert_equal("12345", read_file(path.join(cwd, '1', '2', 'a1.txt'))) 719 | assert_equal("54321", read_file(path.join(cwd, '1', '2', 'a2.txt'))) 720 | assert_false(path.exists(path.join(cwd, '1', '2', '2'))) 721 | assert_false(path.exists(path.join(cwd, '1', '2', 'b1.txt'))) 722 | assert_false(path.exists(path.join(cwd, '1', '2', 'b2.txt'))) 723 | end 724 | 725 | function test_copy_error_skip() 726 | local ivalid_path = path.IS_WINDOWS and path.join(cwd, '1*') or "/dev/qaz" 727 | local options options = { 728 | error = function(err, src, des, opt) 729 | local key = src:upper() 730 | assert_true(files[key]) 731 | assert_equal(options, opt) 732 | files[key] = nil; 733 | return true 734 | end; 735 | } 736 | assert(path.copy( 737 | path.join(cwd, '1', '*'), 738 | ivalid_path, 739 | options 740 | )) 741 | assert_nil(next(files)) 742 | end 743 | 744 | function test_copy_error_break() 745 | local ivalid_path = path.IS_WINDOWS and path.join(cwd, '1*') or "/dev/qaz" 746 | local flag = false 747 | assert(path.copy( 748 | path.join(cwd, '1', '*'), 749 | ivalid_path,{ 750 | error = function() 751 | assert_false(flag) 752 | flag = true 753 | return false 754 | end 755 | } 756 | )) 757 | assert_true(flag) 758 | end 759 | 760 | end 761 | 762 | local _ENV = TEST_CASE('PATH clean dir') if true then 763 | 764 | local cwd 765 | 766 | function teardown() 767 | local print = print 768 | print = (path.remove(path.join(cwd, '1', '2', '3', 'b1.txt'))) 769 | print = (path.remove(path.join(cwd, '1', '2', '3', 'b2.txt'))) 770 | print = (path.remove(path.join(cwd, '1', '2', '3', 'b3.txt'))) 771 | print = (path.remove(path.join(cwd, '1', '2', 'a1.txt'))) 772 | print = (path.remove(path.join(cwd, '1', '2', 'a2.txt'))) 773 | print = (path.remove(path.join(cwd, '1', '2', 'a3.txt'))) 774 | print = (path.remove(path.join(cwd, '1', '2', '3'))) 775 | print = (path.remove(path.join(cwd, '1', '2'))) 776 | print = (path.remove(path.join(cwd, '1'))) 777 | end 778 | 779 | function setup() 780 | cwd = assert_string(path.currentdir()) 781 | teardown() 782 | mkfile(path.join(cwd, '1', '2', '3', 'b1.txt')) 783 | mkfile(path.join(cwd, '1', '2', '3', 'b2.txt')) 784 | mkfile(path.join(cwd, '1', '2', '3', 'b3.txt')) 785 | mkfile(path.join(cwd, '1', '2', 'a1.txt')) 786 | mkfile(path.join(cwd, '1', '2', 'a2.txt')) 787 | mkfile(path.join(cwd, '1', '2', 'a3.txt')) 788 | end 789 | 790 | function test_clean() 791 | assert_equal(8, path.remove(path.join(cwd, "1", "*"), {recurse=true})) 792 | assert_false(path.exists(path.join(cwd, "1", "2"))) 793 | end 794 | 795 | function test_clean_files() 796 | assert_equal(6, path.remove(path.join(cwd, "1", "*"), {skipdirs=true;recurse=true})) 797 | assert(path.exists(path.join(cwd, "1", "2"))) 798 | assert(path.exists(path.join(cwd, "1", "2", "3"))) 799 | assert_false(path.exists(path.join(cwd, "1", "2", "3", "a1.txt"))) 800 | end 801 | 802 | function test_remove() 803 | assert_string(path.exists(path.join(cwd, "1", "2", "a1.txt"))) 804 | assert_string(path.exists(path.join(cwd, "1", "2", "a2.txt"))) 805 | assert_string(path.exists(path.join(cwd, "1", "2", "a3.txt"))) 806 | assert_string(path.exists(path.join(cwd, "1", "2", "3", "b1.txt"))) 807 | assert_string(path.exists(path.join(cwd, "1", "2", "3", "b2.txt"))) 808 | assert_string(path.exists(path.join(cwd, "1", "2", "3", "b3.txt"))) 809 | 810 | assert_equal(2, path.remove(path.join(cwd, "1", "?1.txt"), {recurse=true})) 811 | 812 | assert_false (path.exists(path.join(cwd, "1", "2", "a1.txt"))) 813 | assert_string(path.exists(path.join(cwd, "1", "2", "a2.txt"))) 814 | assert_string(path.exists(path.join(cwd, "1", "2", "a3.txt"))) 815 | assert_false (path.exists(path.join(cwd, "1", "2", "3", "b1.txt"))) 816 | assert_string(path.exists(path.join(cwd, "1", "2", "3", "b2.txt"))) 817 | assert_string(path.exists(path.join(cwd, "1", "2", "3", "b3.txt"))) 818 | end 819 | 820 | function test_remove_accept() 821 | local options options = { 822 | accept = function(src, opt) 823 | local key = src:upper() 824 | assert_equal(options, opt) 825 | return not not path.basename(src):find("^.[12]") 826 | end;recurse = true; 827 | } 828 | assert_equal(4, path.remove(path.join(cwd, "1", "*"), options)) 829 | 830 | assert_false (path.exists(path.join(cwd, "1", "2", "a1.txt"))) 831 | assert_false (path.exists(path.join(cwd, "1", "2", "a2.txt"))) 832 | assert_string(path.exists(path.join(cwd, "1", "2", "a3.txt"))) 833 | assert_false (path.exists(path.join(cwd, "1", "2", "3", "b1.txt"))) 834 | assert_false (path.exists(path.join(cwd, "1", "2", "3", "b2.txt"))) 835 | assert_string(path.exists(path.join(cwd, "1", "2", "3", "b3.txt"))) 836 | end 837 | 838 | function test_remove_error_skip() 839 | local n = 0 840 | assert(path.remove(path.join(cwd, '1', '*'),{ 841 | skipdirs = true; recurse = true; 842 | accept = function(src) 843 | assert(path.remove(src)) 844 | return true 845 | end; 846 | error = function(err, src) 847 | n = n + 1 848 | return true 849 | end; 850 | })) 851 | assert_equal(6, n) 852 | end 853 | 854 | function test_remove_error_break() 855 | local flag = false 856 | assert(path.remove(path.join(cwd, '1', '*'),{ 857 | skipdirs = true; recurse = true; 858 | accept = function(src) 859 | assert(path.remove(src)) 860 | return true 861 | end; 862 | error = function(err, src) 863 | assert_false(false) 864 | flag = true 865 | return false 866 | end; 867 | })) 868 | assert_true(flag) 869 | end 870 | 871 | function test_isempty() 872 | assert_false( path.isempty(path.join(cwd, "1")) ) 873 | assert_equal(8, path.remove(path.join(cwd, "1", "*"), {recurse=true})) 874 | assert_equal(path.join(cwd, "1"), path.exists(path.join(cwd, "1"))) 875 | assert_true(path.isempty(path.join(cwd, "1"))) 876 | end 877 | 878 | end 879 | 880 | local _ENV = TEST_CASE('PATH each mask') if true then 881 | 882 | local cwd, J 883 | 884 | function teardown() 885 | path.remove(J(cwd, '1', '2', 'a1.txt')) 886 | path.remove(J(cwd, '1', '2', 'a2.txt')) 887 | path.remove(J(cwd, '1', '2', 'a3.txt')) 888 | path.remove(J(cwd, '1', '2')) 889 | path.remove(J(cwd, '1')) 890 | end 891 | 892 | function setup() 893 | J = path.join 894 | cwd = assert_string(path.currentdir()) 895 | teardown() 896 | mkfile(J(cwd, '1', '2', 'a1.txt')) 897 | mkfile(J(cwd, '1', '2', 'a2.txt')) 898 | mkfile(J(cwd, '1', '2', 'a3.txt')) 899 | end 900 | 901 | function test_no_mask1() 902 | local mask = path.ensure_dir_end(J(cwd, '1', '2')) 903 | local files = { 904 | [ J(cwd, '1', '2', 'a1.txt') ] = true; 905 | [ J(cwd, '1', '2', 'a2.txt') ] = true; 906 | [ J(cwd, '1', '2', 'a3.txt') ] = true; 907 | } 908 | path.each(mask, function(f) 909 | assert_true(files[f], "unexpected: " .. f) 910 | files[f] = nil 911 | end) 912 | assert_nil(next(files)) 913 | end 914 | 915 | function test_no_mask2() 916 | local mask = J(cwd, '1', '2') 917 | local files = { 918 | [ J(cwd, '1', '2') ] = true; 919 | } 920 | path.each(mask, function(f) 921 | assert_true(files[f], "unexpected: " .. f) 922 | files[f] = nil 923 | end) 924 | assert_nil(next(files)) 925 | end 926 | 927 | end 928 | 929 | if not LUNIT_RUN then lunit.run() end --------------------------------------------------------------------------------