normpath

function normpath( dirty ) --> clean

Description

Returns the shortest clean path name equivalent to dirty path. It applies the following rules iteratively until no further processing can be done:

  1. Multiple Separator elements will be replaced with a single one.

  2. Each "." path name element (the current directory) will be removed.

  3. Removes each inner ".." path element (the parent directory) along with the non-".." element that precedes it.

  4. Removes ".." elements that begin a rooted path: that is, replace "/.." by "/" at the beginning of a path, assuming Separator is "/".

Parameters

dirty

Path with that can have redundand elements.

Return Values

clean

Normalized path without redundant elements. If the result of this process is an empty string, returns the function string ".".

Code

--ZFUNC-normpath-v1
local function normpath( dirty ) --> clean
   --ZFUNC-splitpath-v1
   local function splitpath( path )
      local tab = {}
      for token in string.gmatch( path, "[^/]+" ) do
         if #token > 0 then
            table.insert( tab, token )
         end
      end
      return tab
   end

   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

return normpath

Examples

local t = require( "tapered" )
local normpath = require( "normpath" )

t.is( "a/c", normpath( "a/c" ) )
t.is( "a/c", normpath( "a//c" ) )
t.is( "a/c", normpath( "a/c/." ) )
t.is( "a/c", normpath( "a/c/b/.." ) )
t.is( "/a/c", normpath( "/../a/c" ) )
t.is( "/a/c", normpath( "/../a/b/../././/c" ) )

t.is( "C:/a/b/c", normpath( "C:/a/b_/../b/c_/d_/../.././c" ) )

t.done()