relativepath
function relativepath( from, to ) --> path
Description
Returns a clean relative path between the paths from and to. If both paths are the same returns the function ".".
Parameters
- from
-
The source path.
- to
-
The destination path.
Return Values
- path
-
The relative path between from and to.
Code
local lfs = require( "lfs" ) --ZREQ-lfs --ZFUNC-unixpath-v1 local function unixpath( path ) return path:gsub( "\\", "/" ) end --ZFUNC-currentdir-v1 local function currentdir() local path = lfs.currentdir() return unixpath( path ) end --ZFUNC-rootprefix-v1 local function rootprefix( path ) local remote = path:match[[^//%w+/]] or path:match[[^\\%w+\]] if remote then return remote end local unix = path:sub( 1, 1 ) if unix == "/" then return unix end local win = path:match[=[^[a-zA-Z]:[\/]]=] if win then return win end return "" end --ZFUNC-isabsolute-v1 local function isabsolute( path ) --> res return rootprefix( path ) ~= "" end --ZFUNC-firstchar-v1 local function firstchar( str ) return string.sub( str, 1, 1 ) end --ZFUNC-lastchar-v1 local function lastchar( str ) return string.sub( str, #str ) end --ZFUNC-joinpath-v1 local function joinpath( tab ) local rooted = false local tmptab = {} for k, s in ipairs( tab ) do if k == 1 and firstchar( s ) == "/" then rooted = true end if firstchar( s ) == "/" then s = s:sub( 2 ) end if lastchar( s ) == "/" then s = s:sub( 1, #s - 1 ) end if #s > 0 then table.insert( tmptab, s ) end end if rooted then return "/"..table.concat( tmptab, "/" ) end return table.concat( tmptab, "/" ) end --ZFUNC-splitpath-v1 local function splitpath( path ) --> tab local tab = {} for token in string.gmatch( path, "[^/]+" ) do if #token > 0 then table.insert( tab, token ) end end return tab end --ZFUNC-normpath-v1 local function normpath( dirty ) --> clean if dirty == "" then return "." end local rooted = dirty:sub( 1, 1 ) == "/" local dirtytab = splitpath( dirty ) local cleantab = {} for k, v in ipairs( dirtytab ) do if v == "." then elseif v == ".." then table.remove( cleantab ) elseif v == "" then else table.insert( cleantab, v ) end end if rooted then return "/"..table.concat( cleantab, "/" ) end return table.concat( cleantab, "/" ) end --ZFUNC-abspath-v3 local function abspath( path ) --> abs if isabsolute( path ) then return normpath( path ) end return normpath( joinpath{ currentdir(), path } ) end --ZFUNC-relativepath-v2 local function relativepath( from, to ) --> path from = abspath( from ) to = abspath( to ) if from == to then return "." end local fromtab = splitpath( normpath( from ) ) local totab = splitpath( normpath( to ) ) local f = 1 local t = 1 while f <= #fromtab and t <= #totab and fromtab[ f ] == totab[ t ] do f = f+1 t = t+1 end local pathtab = {} for i = f, #fromtab do table.insert( pathtab, ".." ) end for i = t, #totab do table.insert( pathtab, totab[ i ] ) end if #pathtab == 0 then return "." end return table.concat( pathtab, "/" ) end return relativepath
Examples
local t = require( "taptest") local relativepath = require( "relativepath" ) t( relativepath( "data/dir/test/aaa", "data/dir/impl/bbb" ), "../../impl/bbb" ) t( relativepath( "/the/same/path", "/the/same/path/order/../../path" ), "." ) --https://github.com/nodejs/node/blob/master/test/parallel/test-path.js t( relativepath( "/var/lib", "/var" ), ".." ) t( relativepath( "/var/lib", "/bin" ), "../../bin" ) t( relativepath( "/var/lib", "/var/lib" ), "." ) t( relativepath( "/var/lib", "/var/apache" ), "../apache" ) t( relativepath( "/var/", "/var/lib" ), "lib" ) t( relativepath( "/", "/var/lib" ), "var/lib" ) t( relativepath( "/foo/test", "/foo/test/bar/package.json" ), "bar/package.json" ) t( relativepath( "/Users/a/web/b/test/mails", "/Users/a/web/b" ), "../.." ) t( relativepath( "/foo/bar/baz-quux", "/foo/bar/baz" ), "../baz" ) t( relativepath( "/foo/bar/baz", "/foo/bar/baz-quux" ), "../baz-quux" ) t( relativepath( "/baz-quux", "/baz" ), "../baz" ) t( relativepath( "/baz", "/baz-quux" ), "../baz-quux" ) t()