--[[ Copyright 2014 The Luvit Authors. All Rights Reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. --]] -- Extracted from https://github.com/luvit/luvi/blob/master/src/lua/init.lua local isWindows if jit and jit.os then -- Luajit provides explicit platform detection isWindows = jit.os == "Windows" else -- Normal lua will only have \ for path separator on windows. isWindows = package.config:find("\\") and true or false end local uv = require('luv') local getPrefix, splitPath, joinParts if isWindows then -- Windows aware path utilities function getPrefix(path) return path:match("^%a:\\") or path:match("^/") or path:match("^\\+") end function splitPath(path) local parts = {} for part in string.gmatch(path, '([^/\\]+)') do table.insert(parts, part) end return parts end function joinParts(prefix, parts, i, j) if not prefix then return table.concat(parts, '/', i, j) elseif prefix ~= '/' then return prefix .. table.concat(parts, '\\', i, j) else return prefix .. table.concat(parts, '/', i, j) end end else -- Simple optimized versions for UNIX systems function getPrefix(path) return path:match("^/") end function splitPath(path) local parts = {} for part in string.gmatch(path, '([^/]+)') do table.insert(parts, part) end return parts end function joinParts(prefix, parts, i, j) if prefix then return prefix .. table.concat(parts, '/', i, j) end return table.concat(parts, '/', i, j) end end local function pathJoin(...) local inputs = {...} local l = #inputs -- Find the last segment that is an absolute path -- Or if all are relative, prefix will be nil local i = l local prefix while true do prefix = getPrefix(inputs[i]) if prefix or i <= 1 then break end i = i - 1 end -- If there was one, remove its prefix from its segment if prefix then inputs[i] = inputs[i]:sub(#prefix) end -- Split all the paths segments into one large list local parts = {} while i <= l do local sub = splitPath(inputs[i]) for j = 1, #sub do parts[#parts + 1] = sub[j] end i = i + 1 end -- Evaluate special segments in reverse order. local skip = 0 local reversed = {} for idx = #parts, 1, -1 do local part = parts[idx] if part == '.' then -- Ignore elseif part == '..' then skip = skip + 1 elseif skip > 0 then skip = skip - 1 else reversed[#reversed + 1] = part end end -- Reverse the list again to get the correct order parts = reversed for idx = 1, #parts / 2 do local j = #parts - idx + 1 parts[idx], parts[j] = parts[j], parts[idx] end local path = joinParts(prefix, parts) return path end return { isWindows = isWindows, join = pathJoin, getPrefix = getPrefix, splitPath = splitPath, joinparts = joinParts, }