alunizaje/android/tools/zbstudio.app/Contents/ZeroBraneStudio/lualibs/git/util.lua
Andros Fenollosa 892d89c7f1 Order files
2016-11-12 12:27:08 +01:00

234 lines
5.6 KiB
Lua

local lfs = require 'lfs'
local core = require 'git.core'
local deflate = core.deflate
local inflate = core.inflate
local sha = core.sha
module(..., package.seeall)
local BUF_SIZE = 4096
local dirsep = package.config:sub(1,1)
-- replaces '/' path separators on Windows with the correct ones ('\\')
function correct_separators(path)
return path:gsub('/', dirsep)
end
-- joins several path components into a single path, uses system-specific directory
-- separator, cleans input, i.e. join_path('a/', 'b', 'c/') => 'a/b/c'
function join_path(...)
local n = select('#', ...)
local args = {...}
for i=1,n do
args[i] = args[i]:gsub(dirsep..'?$', '')
end
return table.concat(args, dirsep, 1, n)
end
-- Return the path with the all occurences of '/.' or '\.' (representing
-- the current directory) removed.
local function remove_curr_dir_dots(path)
while path:match(dirsep .. "%." .. dirsep) do -- match("/%./")
path = path:gsub(dirsep .. "%." .. dirsep, dirsep) -- gsub("/%./", "/")
end
return path:gsub(dirsep .. "%.$", "") -- gsub("/%.$", "")
end
-- Return whether the path is a root.
local function is_root(path)
return path:find("^[%u%U.]?:?[/\\]$")
end
-- Return the path with the unnecessary trailing separator removed.
local function remove_trailing(path)
if path:sub(-1) == dirsep and not is_root(path) then path = path:sub(1,-2) end
return path
end
-- Extract file or directory name from its path.
local function extract_name(path)
if is_root(path) then return path end
path = remove_trailing(path)
path = path:gsub("^.*" .. dirsep, "")
return path
end
-- Return the string 'str', with all magic (pattern) characters escaped.
local function escape_magic(str)
local escaped = str:gsub('[%-%.%+%[%]%(%)%^%%%?%*%^%$]','%%%1')
return escaped
end
-- Return parent directory of the 'path' or nil if there's no parent directory.
-- If 'path' is a path to file, return the directory the file is in.
function parent_dir(path)
path = remove_curr_dir_dots(path)
path = remove_trailing(path)
local dir = path:gsub(escape_magic(extract_name(path)) .. "$", "")
if dir == "" then
return nil
else
return remove_trailing(dir)
end
end
-- Make a new directory, making also all of its parent directories that doesn't exist.
function make_dir(path)
if lfs.attributes(path) then
return true
else
local par_dir = parent_dir(path)
if par_dir then
assert(make_dir(par_dir))
end
return lfs.mkdir(path)
end
end
-- Reader class
-- adapted from Penlight: https://raw.github.com/stevedonovan/Penlight/master/lua/pl/stringio.lua
local SR = {}
SR.__index = SR
function SR:_read(fmt)
local i,str = self.i,self.str
local sz = #str
if i > sz then return nil, "past end of file" end
local res
if fmt == '*l' or fmt == '*L' then
local idx = str:find('\n',i) or (sz+1)
res = str:sub(i,fmt == '*l' and idx-1 or idx)
self.i = idx+1
elseif fmt == '*a' then
res = str:sub(i)
self.i = sz+1
elseif fmt == '*n' then
local _,i2,i2,idx
_,idx = str:find ('%s*%d+',i)
_,i2 = str:find ('^%.%d+',idx+1)
if i2 then idx = i2 end
_,i2 = str:find ('^[eE][%+%-]*%d+',idx+1)
if i2 then idx = i2 end
local val = str:sub(i,idx)
res = tonumber(val)
self.i = idx+1
elseif type(fmt) == 'number' then
res = str:sub(i,i+fmt-1)
self.i = i + fmt
else
error("bad read format",2)
end
return res
end
function SR:read(...)
if select('#',...) == 0 then
return self:_read('*l')
else
local res, fmts = {},{...}
for i = 1, #fmts do
res[i] = self:_read(fmts[i])
end
return unpack(res)
end
end
function SR:seek(whence,offset)
local base
whence = whence or 'cur'
offset = offset or 0
if whence == 'set' then
base = 1
elseif whence == 'cur' then
base = self.i
elseif whence == 'end' then
base = #self.str
end
self.i = base + offset
return self.i
end
function SR:close() -- for compatibility only
end
--- create a file-like object for reading from a given string.
-- @param s The input string.
function reader(s)
return setmetatable({str=s,i=1},SR)
end
-- decompress the file and return a handle to temporary uncompressed file
function decompressed(path)
local fi = assert(io.open(path, 'rb'))
local result = {}
local z = inflate()
repeat
local str = fi:read(BUF_SIZE)
local data = z(str)
if type(data) == 'string' then
result[#result+1] = data
else print('!!!', data) end
until not str
fi:close()
return reader(table.concat(result))
end
-- reads until the byte \0, consumes it and returns the string up to the \0
function read_until_nul(f)
local t = {}
repeat
local c = f:read(1)
if c and c ~= '\0' then t[#t+1] = c end
until not c or c == '\0'
if #t > 0 then
return table.concat(t)
else
return nil
end
end
-- converts a string to lowercase hex
function to_hex(s)
return (s:gsub('.', function(c)
return string.format('%02x', string.byte(c))
end))
end
-- converts a string from hex to binary
function from_hex(s)
return (s:gsub('..', function(cc)
return string.char(tonumber(cc, 16))
end))
end
-- always returns readable (hex) hash
function readable_sha(s)
if #s ~= 40 then return to_hex(s)
else return s end
end
-- always returns binary hash
function binary_sha(s)
if #s ~= 20 then return from_hex(s)
else return s end
end
function object_sha(data, len, type)
local header = type .. ' ' .. len .. '\0'
local res = sha(header .. data)
return res
end
function deflate(data)
local c = deflate()
return c(data, "finish")
end