mirror of
https://github.com/FlightControl-Master/MOOSE.git
synced 2025-08-15 10:47:21 +00:00
This is an important refactor of the way documentation generation works
* Installs luarocks WITH it's executable (easy to install other rocks if necessary) * Use Lua supplied with luarocks * Create Utils/luadocumentor.bat, which works with RELATIVE PATH ! -> Everybody can generate the doc * Updated launch files accordingly
This commit is contained in:
700
Utils/luarocks/lua/luarocks/tools/patch.lua
Normal file
700
Utils/luarocks/lua/luarocks/tools/patch.lua
Normal file
@@ -0,0 +1,700 @@
|
||||
--- Patch utility to apply unified diffs.
|
||||
--
|
||||
-- http://lua-users.org/wiki/LuaPatch
|
||||
--
|
||||
-- (c) 2008 David Manura, Licensed under the same terms as Lua (MIT license).
|
||||
-- Code is heavilly based on the Python-based patch.py version 8.06-1
|
||||
-- Copyright (c) 2008 rainforce.org, MIT License
|
||||
-- Project home: http://code.google.com/p/python-patch/ .
|
||||
-- Version 0.1
|
||||
|
||||
local patch = {}
|
||||
|
||||
local fs = require("luarocks.fs")
|
||||
local util = require("luarocks.util")
|
||||
|
||||
local io = io
|
||||
local os = os
|
||||
local string = string
|
||||
local table = table
|
||||
local format = string.format
|
||||
|
||||
-- logging
|
||||
local debugmode = false
|
||||
local function debug(_) end
|
||||
local function info(_) end
|
||||
local function warning(s) io.stderr:write(s .. '\n') end
|
||||
|
||||
-- Returns boolean whether string s2 starts with string s.
|
||||
local function startswith(s, s2)
|
||||
return s:sub(1, #s2) == s2
|
||||
end
|
||||
|
||||
-- Returns boolean whether string s2 ends with string s.
|
||||
local function endswith(s, s2)
|
||||
return #s >= #s2 and s:sub(#s-#s2+1) == s2
|
||||
end
|
||||
|
||||
-- Returns string s after filtering out any new-line characters from end.
|
||||
local function endlstrip(s)
|
||||
return s:gsub('[\r\n]+$', '')
|
||||
end
|
||||
|
||||
-- Returns shallow copy of table t.
|
||||
local function table_copy(t)
|
||||
local t2 = {}
|
||||
for k,v in pairs(t) do t2[k] = v end
|
||||
return t2
|
||||
end
|
||||
|
||||
local function exists(filename)
|
||||
local fh = io.open(filename)
|
||||
local result = fh ~= nil
|
||||
if fh then fh:close() end
|
||||
return result
|
||||
end
|
||||
local function isfile() return true end --FIX?
|
||||
|
||||
local function read_file(filename)
|
||||
local fh, data, err, oserr
|
||||
fh, err, oserr = io.open(filename, 'rb')
|
||||
if not fh then return fh, err, oserr end
|
||||
data, err, oserr = fh:read'*a'
|
||||
fh:close()
|
||||
if not data then return nil, err, oserr end
|
||||
return data
|
||||
end
|
||||
|
||||
local function write_file(filename, data)
|
||||
local fh, status, err, oserr
|
||||
fh, err, oserr = io.open(filename 'wb')
|
||||
if not fh then return fh, err, oserr end
|
||||
status, err, oserr = fh:write(data)
|
||||
fh:close()
|
||||
if not status then return nil, err, oserr end
|
||||
return true
|
||||
end
|
||||
|
||||
local function file_copy(src, dest)
|
||||
local data, status, err, oserr
|
||||
data, err, oserr = read_file(src)
|
||||
if not data then return data, err, oserr end
|
||||
status, err, oserr = write_file(dest)
|
||||
if not status then return status, err, oserr end
|
||||
return true
|
||||
end
|
||||
|
||||
local function string_as_file(s)
|
||||
return {
|
||||
at = 0,
|
||||
str = s,
|
||||
len = #s,
|
||||
eof = false,
|
||||
read = function(self, n)
|
||||
if self.eof then return nil end
|
||||
local chunk = self.str:sub(self.at, self.at + n - 1)
|
||||
self.at = self.at + n
|
||||
if self.at > self.len then
|
||||
self.eof = true
|
||||
end
|
||||
return chunk
|
||||
end,
|
||||
close = function(self)
|
||||
self.eof = true
|
||||
end,
|
||||
}
|
||||
end
|
||||
|
||||
--
|
||||
-- file_lines(f) is similar to f:lines() for file f.
|
||||
-- The main difference is that read_lines includes
|
||||
-- new-line character sequences ("\n", "\r\n", "\r"),
|
||||
-- if any, at the end of each line. Embedded "\0" are also handled.
|
||||
-- Caution: The newline behavior can depend on whether f is opened
|
||||
-- in binary or ASCII mode.
|
||||
-- (file_lines - version 20080913)
|
||||
--
|
||||
local function file_lines(f)
|
||||
local CHUNK_SIZE = 1024
|
||||
local buffer = ""
|
||||
local pos_beg = 1
|
||||
return function()
|
||||
local pos, chars
|
||||
while 1 do
|
||||
pos, chars = buffer:match('()([\r\n].)', pos_beg)
|
||||
if pos or not f then
|
||||
break
|
||||
elseif f then
|
||||
local chunk = f:read(CHUNK_SIZE)
|
||||
if chunk then
|
||||
buffer = buffer:sub(pos_beg) .. chunk
|
||||
pos_beg = 1
|
||||
else
|
||||
f = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
if not pos then
|
||||
pos = #buffer
|
||||
elseif chars == '\r\n' then
|
||||
pos = pos + 1
|
||||
end
|
||||
local line = buffer:sub(pos_beg, pos)
|
||||
pos_beg = pos + 1
|
||||
if #line > 0 then
|
||||
return line
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function match_linerange(line)
|
||||
local m1, m2, m3, m4 = line:match("^@@ %-(%d+),(%d+) %+(%d+),(%d+)")
|
||||
if not m1 then m1, m3, m4 = line:match("^@@ %-(%d+) %+(%d+),(%d+)") end
|
||||
if not m1 then m1, m2, m3 = line:match("^@@ %-(%d+),(%d+) %+(%d+)") end
|
||||
if not m1 then m1, m3 = line:match("^@@ %-(%d+) %+(%d+)") end
|
||||
return m1, m2, m3, m4
|
||||
end
|
||||
|
||||
function patch.read_patch(filename, data)
|
||||
-- define possible file regions that will direct the parser flow
|
||||
local state = 'header'
|
||||
-- 'header' - comments before the patch body
|
||||
-- 'filenames' - lines starting with --- and +++
|
||||
-- 'hunkhead' - @@ -R +R @@ sequence
|
||||
-- 'hunkbody'
|
||||
-- 'hunkskip' - skipping invalid hunk mode
|
||||
|
||||
local all_ok = true
|
||||
local lineends = {lf=0, crlf=0, cr=0}
|
||||
local files = {source={}, target={}, hunks={}, fileends={}, hunkends={}}
|
||||
local nextfileno = 0
|
||||
local nexthunkno = 0 --: even if index starts with 0 user messages
|
||||
-- number hunks from 1
|
||||
|
||||
-- hunkinfo holds parsed values, hunkactual - calculated
|
||||
local hunkinfo = {
|
||||
startsrc=nil, linessrc=nil, starttgt=nil, linestgt=nil,
|
||||
invalid=false, text={}
|
||||
}
|
||||
local hunkactual = {linessrc=nil, linestgt=nil}
|
||||
|
||||
info(format("reading patch %s", filename))
|
||||
|
||||
local fp
|
||||
if data then
|
||||
fp = string_as_file(data)
|
||||
else
|
||||
fp = filename == '-' and io.stdin or assert(io.open(filename, "rb"))
|
||||
end
|
||||
local lineno = 0
|
||||
|
||||
for line in file_lines(fp) do
|
||||
lineno = lineno + 1
|
||||
if state == 'header' then
|
||||
if startswith(line, "--- ") then
|
||||
state = 'filenames'
|
||||
end
|
||||
-- state is 'header' or 'filenames'
|
||||
end
|
||||
if state == 'hunkbody' then
|
||||
-- skip hunkskip and hunkbody code until definition of hunkhead read
|
||||
|
||||
if line:match"^[\r\n]*$" then
|
||||
-- prepend space to empty lines to interpret them as context properly
|
||||
line = " " .. line
|
||||
end
|
||||
|
||||
-- process line first
|
||||
if line:match"^[- +\\]" then
|
||||
-- gather stats about line endings
|
||||
local he = files.hunkends[nextfileno]
|
||||
if endswith(line, "\r\n") then
|
||||
he.crlf = he.crlf + 1
|
||||
elseif endswith(line, "\n") then
|
||||
he.lf = he.lf + 1
|
||||
elseif endswith(line, "\r") then
|
||||
he.cr = he.cr + 1
|
||||
end
|
||||
if startswith(line, "-") then
|
||||
hunkactual.linessrc = hunkactual.linessrc + 1
|
||||
elseif startswith(line, "+") then
|
||||
hunkactual.linestgt = hunkactual.linestgt + 1
|
||||
elseif startswith(line, "\\") then
|
||||
-- nothing
|
||||
else
|
||||
hunkactual.linessrc = hunkactual.linessrc + 1
|
||||
hunkactual.linestgt = hunkactual.linestgt + 1
|
||||
end
|
||||
table.insert(hunkinfo.text, line)
|
||||
-- todo: handle \ No newline cases
|
||||
else
|
||||
warning(format("invalid hunk no.%d at %d for target file %s",
|
||||
nexthunkno, lineno, files.target[nextfileno]))
|
||||
-- add hunk status node
|
||||
table.insert(files.hunks[nextfileno], table_copy(hunkinfo))
|
||||
files.hunks[nextfileno][nexthunkno].invalid = true
|
||||
all_ok = false
|
||||
state = 'hunkskip'
|
||||
end
|
||||
|
||||
-- check exit conditions
|
||||
if hunkactual.linessrc > hunkinfo.linessrc or
|
||||
hunkactual.linestgt > hunkinfo.linestgt
|
||||
then
|
||||
warning(format("extra hunk no.%d lines at %d for target %s",
|
||||
nexthunkno, lineno, files.target[nextfileno]))
|
||||
-- add hunk status node
|
||||
table.insert(files.hunks[nextfileno], table_copy(hunkinfo))
|
||||
files.hunks[nextfileno][nexthunkno].invalid = true
|
||||
state = 'hunkskip'
|
||||
elseif hunkinfo.linessrc == hunkactual.linessrc and
|
||||
hunkinfo.linestgt == hunkactual.linestgt
|
||||
then
|
||||
table.insert(files.hunks[nextfileno], table_copy(hunkinfo))
|
||||
state = 'hunkskip'
|
||||
|
||||
-- detect mixed window/unix line ends
|
||||
local ends = files.hunkends[nextfileno]
|
||||
if (ends.cr~=0 and 1 or 0) + (ends.crlf~=0 and 1 or 0) +
|
||||
(ends.lf~=0 and 1 or 0) > 1
|
||||
then
|
||||
warning(format("inconsistent line ends in patch hunks for %s",
|
||||
files.source[nextfileno]))
|
||||
end
|
||||
if debugmode then
|
||||
local debuglines = {crlf=ends.crlf, lf=ends.lf, cr=ends.cr,
|
||||
file=files.target[nextfileno], hunk=nexthunkno}
|
||||
debug(format("crlf: %(crlf)d lf: %(lf)d cr: %(cr)d\t " ..
|
||||
"- file: %(file)s hunk: %(hunk)d", debuglines))
|
||||
end
|
||||
end
|
||||
-- state is 'hunkbody' or 'hunkskip'
|
||||
end
|
||||
|
||||
if state == 'hunkskip' then
|
||||
if match_linerange(line) then
|
||||
state = 'hunkhead'
|
||||
elseif startswith(line, "--- ") then
|
||||
state = 'filenames'
|
||||
if debugmode and #files.source > 0 then
|
||||
debug(format("- %2d hunks for %s", #files.hunks[nextfileno],
|
||||
files.source[nextfileno]))
|
||||
end
|
||||
end
|
||||
-- state is 'hunkskip', 'hunkhead', or 'filenames'
|
||||
end
|
||||
local advance
|
||||
if state == 'filenames' then
|
||||
if startswith(line, "--- ") then
|
||||
if util.array_contains(files.source, nextfileno) then
|
||||
all_ok = false
|
||||
warning(format("skipping invalid patch for %s",
|
||||
files.source[nextfileno+1]))
|
||||
table.remove(files.source, nextfileno+1)
|
||||
-- double source filename line is encountered
|
||||
-- attempt to restart from this second line
|
||||
end
|
||||
-- Accept a space as a terminator, like GNU patch does.
|
||||
-- Breaks patches containing filenames with spaces...
|
||||
-- FIXME Figure out what does GNU patch do in those cases.
|
||||
local match = line:match("^%-%-%- ([^ \t\r\n]+)")
|
||||
if not match then
|
||||
all_ok = false
|
||||
warning(format("skipping invalid filename at line %d", lineno+1))
|
||||
state = 'header'
|
||||
else
|
||||
table.insert(files.source, match)
|
||||
end
|
||||
elseif not startswith(line, "+++ ") then
|
||||
if util.array_contains(files.source, nextfileno) then
|
||||
all_ok = false
|
||||
warning(format("skipping invalid patch with no target for %s",
|
||||
files.source[nextfileno+1]))
|
||||
table.remove(files.source, nextfileno+1)
|
||||
else
|
||||
-- this should be unreachable
|
||||
warning("skipping invalid target patch")
|
||||
end
|
||||
state = 'header'
|
||||
else
|
||||
if util.array_contains(files.target, nextfileno) then
|
||||
all_ok = false
|
||||
warning(format("skipping invalid patch - double target at line %d",
|
||||
lineno+1))
|
||||
table.remove(files.source, nextfileno+1)
|
||||
table.remove(files.target, nextfileno+1)
|
||||
nextfileno = nextfileno - 1
|
||||
-- double target filename line is encountered
|
||||
-- switch back to header state
|
||||
state = 'header'
|
||||
else
|
||||
-- Accept a space as a terminator, like GNU patch does.
|
||||
-- Breaks patches containing filenames with spaces...
|
||||
-- FIXME Figure out what does GNU patch do in those cases.
|
||||
local re_filename = "^%+%+%+ ([^ \t\r\n]+)"
|
||||
local match = line:match(re_filename)
|
||||
if not match then
|
||||
all_ok = false
|
||||
warning(format(
|
||||
"skipping invalid patch - no target filename at line %d",
|
||||
lineno+1))
|
||||
state = 'header'
|
||||
else
|
||||
table.insert(files.target, match)
|
||||
nextfileno = nextfileno + 1
|
||||
nexthunkno = 0
|
||||
table.insert(files.hunks, {})
|
||||
table.insert(files.hunkends, table_copy(lineends))
|
||||
table.insert(files.fileends, table_copy(lineends))
|
||||
state = 'hunkhead'
|
||||
advance = true
|
||||
end
|
||||
end
|
||||
end
|
||||
-- state is 'filenames', 'header', or ('hunkhead' with advance)
|
||||
end
|
||||
if not advance and state == 'hunkhead' then
|
||||
local m1, m2, m3, m4 = match_linerange(line)
|
||||
if not m1 then
|
||||
if not util.array_contains(files.hunks, nextfileno-1) then
|
||||
all_ok = false
|
||||
warning(format("skipping invalid patch with no hunks for file %s",
|
||||
files.target[nextfileno]))
|
||||
end
|
||||
state = 'header'
|
||||
else
|
||||
hunkinfo.startsrc = tonumber(m1)
|
||||
hunkinfo.linessrc = tonumber(m2 or 1)
|
||||
hunkinfo.starttgt = tonumber(m3)
|
||||
hunkinfo.linestgt = tonumber(m4 or 1)
|
||||
hunkinfo.invalid = false
|
||||
hunkinfo.text = {}
|
||||
|
||||
hunkactual.linessrc = 0
|
||||
hunkactual.linestgt = 0
|
||||
|
||||
state = 'hunkbody'
|
||||
nexthunkno = nexthunkno + 1
|
||||
end
|
||||
-- state is 'header' or 'hunkbody'
|
||||
end
|
||||
end
|
||||
if state ~= 'hunkskip' then
|
||||
warning(format("patch file incomplete - %s", filename))
|
||||
all_ok = false
|
||||
-- os.exit(?)
|
||||
else
|
||||
-- duplicated message when an eof is reached
|
||||
if debugmode and #files.source > 0 then
|
||||
debug(format("- %2d hunks for %s", #files.hunks[nextfileno],
|
||||
files.source[nextfileno]))
|
||||
end
|
||||
end
|
||||
|
||||
local sum = 0; for _,hset in ipairs(files.hunks) do sum = sum + #hset end
|
||||
info(format("total files: %d total hunks: %d", #files.source, sum))
|
||||
fp:close()
|
||||
return files, all_ok
|
||||
end
|
||||
|
||||
local function find_hunk(file, h, hno)
|
||||
for fuzz=0,2 do
|
||||
local lineno = h.startsrc
|
||||
for i=0,#file do
|
||||
local found = true
|
||||
local location = lineno
|
||||
for l, hline in ipairs(h.text) do
|
||||
if l > fuzz then
|
||||
-- todo: \ No newline at the end of file
|
||||
if startswith(hline, " ") or startswith(hline, "-") then
|
||||
local line = file[lineno]
|
||||
lineno = lineno + 1
|
||||
if not line or #line == 0 then
|
||||
found = false
|
||||
break
|
||||
end
|
||||
if endlstrip(line) ~= endlstrip(hline:sub(2)) then
|
||||
found = false
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
if found then
|
||||
local offset = location - h.startsrc - fuzz
|
||||
if offset ~= 0 then
|
||||
warning(format("Hunk %d found at offset %d%s...", hno, offset, fuzz == 0 and "" or format(" (fuzz %d)", fuzz)))
|
||||
end
|
||||
h.startsrc = location
|
||||
h.starttgt = h.starttgt + offset
|
||||
for _=1,fuzz do
|
||||
table.remove(h.text, 1)
|
||||
table.remove(h.text, #h.text)
|
||||
end
|
||||
return true
|
||||
end
|
||||
lineno = i
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
local function load_file(filename)
|
||||
local fp = assert(io.open(filename))
|
||||
local file = {}
|
||||
local readline = file_lines(fp)
|
||||
while true do
|
||||
local line = readline()
|
||||
if not line then break end
|
||||
table.insert(file, line)
|
||||
end
|
||||
fp:close()
|
||||
return file
|
||||
end
|
||||
|
||||
local function find_hunks(file, hunks)
|
||||
for hno, h in ipairs(hunks) do
|
||||
find_hunk(file, h, hno)
|
||||
end
|
||||
end
|
||||
|
||||
local function check_patched(file, hunks)
|
||||
local lineno = 1
|
||||
local ok, err = pcall(function()
|
||||
if #file == 0 then
|
||||
error('nomatch', 0)
|
||||
end
|
||||
for hno, h in ipairs(hunks) do
|
||||
-- skip to line just before hunk starts
|
||||
if #file < h.starttgt then
|
||||
error('nomatch', 0)
|
||||
end
|
||||
lineno = h.starttgt
|
||||
for _, hline in ipairs(h.text) do
|
||||
-- todo: \ No newline at the end of file
|
||||
if not startswith(hline, "-") and not startswith(hline, "\\") then
|
||||
local line = file[lineno]
|
||||
lineno = lineno + 1
|
||||
if #line == 0 then
|
||||
error('nomatch', 0)
|
||||
end
|
||||
if endlstrip(line) ~= endlstrip(hline:sub(2)) then
|
||||
warning(format("file is not patched - failed hunk: %d", hno))
|
||||
error('nomatch', 0)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
-- todo: display failed hunk, i.e. expected/found
|
||||
return err ~= 'nomatch'
|
||||
end
|
||||
|
||||
local function patch_hunks(srcname, tgtname, hunks)
|
||||
local src = assert(io.open(srcname, "rb"))
|
||||
local tgt = assert(io.open(tgtname, "wb"))
|
||||
|
||||
local src_readline = file_lines(src)
|
||||
|
||||
-- todo: detect linefeeds early - in apply_files routine
|
||||
-- to handle cases when patch starts right from the first
|
||||
-- line and no lines are processed. At the moment substituted
|
||||
-- lineends may not be the same at the start and at the end
|
||||
-- of patching. Also issue a warning about mixed lineends
|
||||
|
||||
local srclineno = 1
|
||||
local lineends = {['\n']=0, ['\r\n']=0, ['\r']=0}
|
||||
for hno, h in ipairs(hunks) do
|
||||
debug(format("processing hunk %d for file %s", hno, tgtname))
|
||||
-- skip to line just before hunk starts
|
||||
while srclineno < h.startsrc do
|
||||
local line = src_readline()
|
||||
-- Python 'U' mode works only with text files
|
||||
if endswith(line, "\r\n") then
|
||||
lineends["\r\n"] = lineends["\r\n"] + 1
|
||||
elseif endswith(line, "\n") then
|
||||
lineends["\n"] = lineends["\n"] + 1
|
||||
elseif endswith(line, "\r") then
|
||||
lineends["\r"] = lineends["\r"] + 1
|
||||
end
|
||||
tgt:write(line)
|
||||
srclineno = srclineno + 1
|
||||
end
|
||||
|
||||
for _,hline in ipairs(h.text) do
|
||||
-- todo: check \ No newline at the end of file
|
||||
if startswith(hline, "-") or startswith(hline, "\\") then
|
||||
src_readline()
|
||||
srclineno = srclineno + 1
|
||||
else
|
||||
if not startswith(hline, "+") then
|
||||
src_readline()
|
||||
srclineno = srclineno + 1
|
||||
end
|
||||
local line2write = hline:sub(2)
|
||||
-- detect if line ends are consistent in source file
|
||||
local sum = 0
|
||||
for _,v in pairs(lineends) do if v > 0 then sum=sum+1 end end
|
||||
if sum == 1 then
|
||||
local newline
|
||||
for k,v in pairs(lineends) do if v ~= 0 then newline = k end end
|
||||
tgt:write(endlstrip(line2write) .. newline)
|
||||
else -- newlines are mixed or unknown
|
||||
tgt:write(line2write)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
for line in src_readline do
|
||||
tgt:write(line)
|
||||
end
|
||||
tgt:close()
|
||||
src:close()
|
||||
return true
|
||||
end
|
||||
|
||||
local function strip_dirs(filename, strip)
|
||||
if strip == nil then return filename end
|
||||
for _=1,strip do
|
||||
filename=filename:gsub("^[^/]*/", "")
|
||||
end
|
||||
return filename
|
||||
end
|
||||
|
||||
function patch.apply_patch(the_patch, strip)
|
||||
local all_ok = true
|
||||
local total = #the_patch.source
|
||||
for fileno, filename in ipairs(the_patch.source) do
|
||||
filename = strip_dirs(filename, strip)
|
||||
local continue
|
||||
local f2patch = filename
|
||||
if not exists(f2patch) then
|
||||
f2patch = strip_dirs(the_patch.target[fileno], strip)
|
||||
f2patch = fs.absolute_name(f2patch)
|
||||
if not exists(f2patch) then --FIX:if f2patch nil
|
||||
warning(format("source/target file does not exist\n--- %s\n+++ %s",
|
||||
filename, f2patch))
|
||||
all_ok = false
|
||||
continue = true
|
||||
end
|
||||
end
|
||||
if not continue and not isfile(f2patch) then
|
||||
warning(format("not a file - %s", f2patch))
|
||||
all_ok = false
|
||||
continue = true
|
||||
end
|
||||
if not continue then
|
||||
|
||||
filename = f2patch
|
||||
|
||||
info(format("processing %d/%d:\t %s", fileno, total, filename))
|
||||
|
||||
-- validate before patching
|
||||
local hunks = the_patch.hunks[fileno]
|
||||
local file = load_file(filename)
|
||||
local hunkno = 1
|
||||
local hunk = hunks[hunkno]
|
||||
local hunkfind = {}
|
||||
local validhunks = 0
|
||||
local canpatch = false
|
||||
local hunklineno
|
||||
local isbreak
|
||||
local lineno = 0
|
||||
|
||||
find_hunks(file, hunks)
|
||||
|
||||
for _, line in ipairs(file) do
|
||||
lineno = lineno + 1
|
||||
local continue
|
||||
if not hunk or lineno < hunk.startsrc then
|
||||
continue = true
|
||||
elseif lineno == hunk.startsrc then
|
||||
hunkfind = {}
|
||||
for _,x in ipairs(hunk.text) do
|
||||
if x:sub(1,1) == ' ' or x:sub(1,1) == '-' then
|
||||
hunkfind[#hunkfind+1] = endlstrip(x:sub(2))
|
||||
end
|
||||
end
|
||||
hunklineno = 1
|
||||
|
||||
-- todo \ No newline at end of file
|
||||
end
|
||||
-- check hunks in source file
|
||||
if not continue and lineno < hunk.startsrc + #hunkfind - 1 then
|
||||
if endlstrip(line) == hunkfind[hunklineno] then
|
||||
hunklineno = hunklineno + 1
|
||||
else
|
||||
debug(format("hunk no.%d doesn't match source file %s",
|
||||
hunkno, filename))
|
||||
-- file may be already patched, but check other hunks anyway
|
||||
hunkno = hunkno + 1
|
||||
if hunkno <= #hunks then
|
||||
hunk = hunks[hunkno]
|
||||
continue = true
|
||||
else
|
||||
isbreak = true; break
|
||||
end
|
||||
end
|
||||
end
|
||||
-- check if processed line is the last line
|
||||
if not continue and lineno == hunk.startsrc + #hunkfind - 1 then
|
||||
debug(format("file %s hunk no.%d -- is ready to be patched",
|
||||
filename, hunkno))
|
||||
hunkno = hunkno + 1
|
||||
validhunks = validhunks + 1
|
||||
if hunkno <= #hunks then
|
||||
hunk = hunks[hunkno]
|
||||
else
|
||||
if validhunks == #hunks then
|
||||
-- patch file
|
||||
canpatch = true
|
||||
isbreak = true; break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
if not isbreak then
|
||||
if hunkno <= #hunks then
|
||||
warning(format("premature end of source file %s at hunk %d",
|
||||
filename, hunkno))
|
||||
all_ok = false
|
||||
end
|
||||
end
|
||||
if validhunks < #hunks then
|
||||
if check_patched(file, hunks) then
|
||||
warning(format("already patched %s", filename))
|
||||
else
|
||||
warning(format("source file is different - %s", filename))
|
||||
all_ok = false
|
||||
end
|
||||
end
|
||||
if canpatch then
|
||||
local backupname = filename .. ".orig"
|
||||
if exists(backupname) then
|
||||
warning(format("can't backup original file to %s - aborting",
|
||||
backupname))
|
||||
all_ok = false
|
||||
else
|
||||
assert(os.rename(filename, backupname))
|
||||
if patch_hunks(backupname, filename, hunks) then
|
||||
warning(format("successfully patched %s", filename))
|
||||
assert(os.remove(backupname))
|
||||
else
|
||||
warning(format("error patching file %s", filename))
|
||||
assert(file_copy(filename, filename .. ".invalid"))
|
||||
warning(format("invalid version is saved to %s",
|
||||
filename .. ".invalid"))
|
||||
-- todo: proper rejects
|
||||
assert(os.rename(backupname, filename))
|
||||
all_ok = false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end -- if not continue
|
||||
end -- for
|
||||
-- todo: check for premature eof
|
||||
return all_ok
|
||||
end
|
||||
|
||||
return patch
|
||||
150
Utils/luarocks/lua/luarocks/tools/tar.lua
Normal file
150
Utils/luarocks/lua/luarocks/tools/tar.lua
Normal file
@@ -0,0 +1,150 @@
|
||||
|
||||
--- A pure-Lua implementation of untar (unpacking .tar archives)
|
||||
local tar = {}
|
||||
|
||||
local fs = require("luarocks.fs")
|
||||
local dir = require("luarocks.dir")
|
||||
local util = require("luarocks.util")
|
||||
|
||||
local blocksize = 512
|
||||
|
||||
local function get_typeflag(flag)
|
||||
if flag == "0" or flag == "\0" then return "file"
|
||||
elseif flag == "1" then return "link"
|
||||
elseif flag == "2" then return "symlink" -- "reserved" in POSIX, "symlink" in GNU
|
||||
elseif flag == "3" then return "character"
|
||||
elseif flag == "4" then return "block"
|
||||
elseif flag == "5" then return "directory"
|
||||
elseif flag == "6" then return "fifo"
|
||||
elseif flag == "7" then return "contiguous" -- "reserved" in POSIX, "contiguous" in GNU
|
||||
elseif flag == "x" then return "next file"
|
||||
elseif flag == "g" then return "global extended header"
|
||||
elseif flag == "L" then return "long name"
|
||||
elseif flag == "K" then return "long link name"
|
||||
end
|
||||
return "unknown"
|
||||
end
|
||||
|
||||
local function octal_to_number(octal)
|
||||
local exp = 0
|
||||
local number = 0
|
||||
for i = #octal,1,-1 do
|
||||
local digit = tonumber(octal:sub(i,i))
|
||||
if digit then
|
||||
number = number + (digit * 8^exp)
|
||||
exp = exp + 1
|
||||
end
|
||||
end
|
||||
return number
|
||||
end
|
||||
|
||||
local function checksum_header(block)
|
||||
local sum = 256
|
||||
for i = 1,148 do
|
||||
sum = sum + block:byte(i)
|
||||
end
|
||||
for i = 157,500 do
|
||||
sum = sum + block:byte(i)
|
||||
end
|
||||
return sum
|
||||
end
|
||||
|
||||
local function nullterm(s)
|
||||
return s:match("^[^%z]*")
|
||||
end
|
||||
|
||||
local function read_header_block(block)
|
||||
local header = {}
|
||||
header.name = nullterm(block:sub(1,100))
|
||||
header.mode = nullterm(block:sub(101,108))
|
||||
header.uid = octal_to_number(nullterm(block:sub(109,116)))
|
||||
header.gid = octal_to_number(nullterm(block:sub(117,124)))
|
||||
header.size = octal_to_number(nullterm(block:sub(125,136)))
|
||||
header.mtime = octal_to_number(nullterm(block:sub(137,148)))
|
||||
header.chksum = octal_to_number(nullterm(block:sub(149,156)))
|
||||
header.typeflag = get_typeflag(block:sub(157,157))
|
||||
header.linkname = nullterm(block:sub(158,257))
|
||||
header.magic = block:sub(258,263)
|
||||
header.version = block:sub(264,265)
|
||||
header.uname = nullterm(block:sub(266,297))
|
||||
header.gname = nullterm(block:sub(298,329))
|
||||
header.devmajor = octal_to_number(nullterm(block:sub(330,337)))
|
||||
header.devminor = octal_to_number(nullterm(block:sub(338,345)))
|
||||
header.prefix = block:sub(346,500)
|
||||
if header.magic ~= "ustar " and header.magic ~= "ustar\0" then
|
||||
return false, "Invalid header magic "..header.magic
|
||||
end
|
||||
if header.version ~= "00" and header.version ~= " \0" then
|
||||
return false, "Unknown version "..header.version
|
||||
end
|
||||
if not checksum_header(block) == header.chksum then
|
||||
return false, "Failed header checksum"
|
||||
end
|
||||
return header
|
||||
end
|
||||
|
||||
function tar.untar(filename, destdir)
|
||||
assert(type(filename) == "string")
|
||||
assert(type(destdir) == "string")
|
||||
|
||||
local tar_handle = io.open(filename, "r")
|
||||
if not tar_handle then return nil, "Error opening file "..filename end
|
||||
|
||||
local long_name, long_link_name
|
||||
while true do
|
||||
local block
|
||||
repeat
|
||||
block = tar_handle:read(blocksize)
|
||||
until (not block) or checksum_header(block) > 256
|
||||
if not block then break end
|
||||
local header, err = read_header_block(block)
|
||||
if not header then
|
||||
util.printerr(err)
|
||||
end
|
||||
|
||||
local file_data = tar_handle:read(math.ceil(header.size / blocksize) * blocksize):sub(1,header.size)
|
||||
|
||||
if header.typeflag == "long name" then
|
||||
long_name = nullterm(file_data)
|
||||
elseif header.typeflag == "long link name" then
|
||||
long_link_name = nullterm(file_data)
|
||||
else
|
||||
if long_name then
|
||||
header.name = long_name
|
||||
long_name = nil
|
||||
end
|
||||
if long_link_name then
|
||||
header.name = long_link_name
|
||||
long_link_name = nil
|
||||
end
|
||||
end
|
||||
local pathname = dir.path(destdir, header.name)
|
||||
if header.typeflag == "directory" then
|
||||
local ok, err = fs.make_dir(pathname)
|
||||
if not ok then return nil, err end
|
||||
elseif header.typeflag == "file" then
|
||||
local dirname = dir.dir_name(pathname)
|
||||
if dirname ~= "" then
|
||||
local ok, err = fs.make_dir(dirname)
|
||||
if not ok then return nil, err end
|
||||
end
|
||||
local file_handle = io.open(pathname, "wb")
|
||||
file_handle:write(file_data)
|
||||
file_handle:close()
|
||||
fs.set_time(pathname, header.mtime)
|
||||
if fs.chmod then
|
||||
fs.chmod(pathname, header.mode)
|
||||
end
|
||||
end
|
||||
--[[
|
||||
for k,v in pairs(header) do
|
||||
util.printout("[\""..tostring(k).."\"] = "..(type(v)=="number" and v or "\""..v:gsub("%z", "\\0").."\""))
|
||||
end
|
||||
util.printout()
|
||||
--]]
|
||||
end
|
||||
tar_handle:close()
|
||||
return true
|
||||
end
|
||||
|
||||
return tar
|
||||
264
Utils/luarocks/lua/luarocks/tools/zip.lua
Normal file
264
Utils/luarocks/lua/luarocks/tools/zip.lua
Normal file
@@ -0,0 +1,264 @@
|
||||
|
||||
--- A Lua implementation of .zip file archiving (used for creating .rock files),
|
||||
-- using only lzlib or lua-lzib.
|
||||
local zip = {}
|
||||
|
||||
local zlib = require("zlib")
|
||||
local fs = require("luarocks.fs")
|
||||
local dir = require("luarocks.dir")
|
||||
|
||||
-- zlib module can be provided by both lzlib and lua-lzib packages.
|
||||
-- Create a compatibility layer.
|
||||
local zlib_compress, zlib_crc32
|
||||
if zlib._VERSION:match "^lua%-zlib" then
|
||||
function zlib_compress(data)
|
||||
return (zlib.deflate()(data, "finish"))
|
||||
end
|
||||
|
||||
function zlib_crc32(data)
|
||||
return zlib.crc32()(data)
|
||||
end
|
||||
elseif zlib._VERSION:match "^lzlib" then
|
||||
function zlib_compress(data)
|
||||
return zlib.compress(data)
|
||||
end
|
||||
|
||||
function zlib_crc32(data)
|
||||
return zlib.crc32(zlib.crc32(), data)
|
||||
end
|
||||
else
|
||||
error("unknown zlib library", 0)
|
||||
end
|
||||
|
||||
local function number_to_bytestring(number, nbytes)
|
||||
local out = {}
|
||||
for _ = 1, nbytes do
|
||||
local byte = number % 256
|
||||
table.insert(out, string.char(byte))
|
||||
number = (number - byte) / 256
|
||||
end
|
||||
return table.concat(out)
|
||||
end
|
||||
|
||||
--- Begin a new file to be stored inside the zipfile.
|
||||
-- @param self handle of the zipfile being written.
|
||||
-- @param filename filenome of the file to be added to the zipfile.
|
||||
-- @return true if succeeded, nil in case of failure.
|
||||
local function zipwriter_open_new_file_in_zip(self, filename)
|
||||
if self.in_open_file then
|
||||
self:close_file_in_zip()
|
||||
return nil
|
||||
end
|
||||
local lfh = {}
|
||||
self.local_file_header = lfh
|
||||
lfh.last_mod_file_time = 0 -- TODO
|
||||
lfh.last_mod_file_date = 0 -- TODO
|
||||
lfh.file_name_length = #filename
|
||||
lfh.extra_field_length = 0
|
||||
lfh.file_name = filename:gsub("\\", "/")
|
||||
lfh.external_attr = 0 -- TODO properly store permissions
|
||||
self.in_open_file = true
|
||||
return true
|
||||
end
|
||||
|
||||
--- Write data to the file currently being stored in the zipfile.
|
||||
-- @param self handle of the zipfile being written.
|
||||
-- @param data string containing full contents of the file.
|
||||
-- @return true if succeeded, nil in case of failure.
|
||||
local function zipwriter_write_file_in_zip(self, data)
|
||||
if not self.in_open_file then
|
||||
return nil
|
||||
end
|
||||
local lfh = self.local_file_header
|
||||
local compressed = zlib_compress(data):sub(3, -5)
|
||||
lfh.crc32 = zlib_crc32(data)
|
||||
lfh.compressed_size = #compressed
|
||||
lfh.uncompressed_size = #data
|
||||
self.data = compressed
|
||||
return true
|
||||
end
|
||||
|
||||
--- Complete the writing of a file stored in the zipfile.
|
||||
-- @param self handle of the zipfile being written.
|
||||
-- @return true if succeeded, nil in case of failure.
|
||||
local function zipwriter_close_file_in_zip(self)
|
||||
local zh = self.ziphandle
|
||||
|
||||
if not self.in_open_file then
|
||||
return nil
|
||||
end
|
||||
|
||||
-- Local file header
|
||||
local lfh = self.local_file_header
|
||||
lfh.offset = zh:seek()
|
||||
zh:write(number_to_bytestring(0x04034b50, 4)) -- signature
|
||||
zh:write(number_to_bytestring(20, 2)) -- version needed to extract: 2.0
|
||||
zh:write(number_to_bytestring(0, 2)) -- general purpose bit flag
|
||||
zh:write(number_to_bytestring(8, 2)) -- compression method: deflate
|
||||
zh:write(number_to_bytestring(lfh.last_mod_file_time, 2))
|
||||
zh:write(number_to_bytestring(lfh.last_mod_file_date, 2))
|
||||
zh:write(number_to_bytestring(lfh.crc32, 4))
|
||||
zh:write(number_to_bytestring(lfh.compressed_size, 4))
|
||||
zh:write(number_to_bytestring(lfh.uncompressed_size, 4))
|
||||
zh:write(number_to_bytestring(lfh.file_name_length, 2))
|
||||
zh:write(number_to_bytestring(lfh.extra_field_length, 2))
|
||||
zh:write(lfh.file_name)
|
||||
|
||||
-- File data
|
||||
zh:write(self.data)
|
||||
|
||||
-- Data descriptor
|
||||
zh:write(number_to_bytestring(lfh.crc32, 4))
|
||||
zh:write(number_to_bytestring(lfh.compressed_size, 4))
|
||||
zh:write(number_to_bytestring(lfh.uncompressed_size, 4))
|
||||
|
||||
table.insert(self.files, lfh)
|
||||
self.in_open_file = false
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
-- @return boolean or (boolean, string): true on success,
|
||||
-- false and an error message on failure.
|
||||
local function zipwriter_add(self, file)
|
||||
local fin
|
||||
local ok, err = self:open_new_file_in_zip(file)
|
||||
if not ok then
|
||||
err = "error in opening "..file.." in zipfile"
|
||||
else
|
||||
fin = io.open(fs.absolute_name(file), "rb")
|
||||
if not fin then
|
||||
ok = false
|
||||
err = "error opening "..file.." for reading"
|
||||
end
|
||||
end
|
||||
if ok then
|
||||
local data = fin:read("*a")
|
||||
if not data then
|
||||
err = "error reading "..file
|
||||
ok = false
|
||||
else
|
||||
ok = self:write_file_in_zip(data)
|
||||
if not ok then
|
||||
err = "error in writing "..file.." in the zipfile"
|
||||
end
|
||||
end
|
||||
end
|
||||
if fin then
|
||||
fin:close()
|
||||
end
|
||||
if ok then
|
||||
ok = self:close_file_in_zip()
|
||||
if not ok then
|
||||
err = "error in writing "..file.." in the zipfile"
|
||||
end
|
||||
end
|
||||
return ok == true, err
|
||||
end
|
||||
|
||||
--- Complete the writing of the zipfile.
|
||||
-- @param self handle of the zipfile being written.
|
||||
-- @return true if succeeded, nil in case of failure.
|
||||
local function zipwriter_close(self)
|
||||
local zh = self.ziphandle
|
||||
|
||||
local central_directory_offset = zh:seek()
|
||||
|
||||
local size_of_central_directory = 0
|
||||
-- Central directory structure
|
||||
for _, lfh in ipairs(self.files) do
|
||||
zh:write(number_to_bytestring(0x02014b50, 4)) -- signature
|
||||
zh:write(number_to_bytestring(3, 2)) -- version made by: UNIX
|
||||
zh:write(number_to_bytestring(20, 2)) -- version needed to extract: 2.0
|
||||
zh:write(number_to_bytestring(0, 2)) -- general purpose bit flag
|
||||
zh:write(number_to_bytestring(8, 2)) -- compression method: deflate
|
||||
zh:write(number_to_bytestring(lfh.last_mod_file_time, 2))
|
||||
zh:write(number_to_bytestring(lfh.last_mod_file_date, 2))
|
||||
zh:write(number_to_bytestring(lfh.crc32, 4))
|
||||
zh:write(number_to_bytestring(lfh.compressed_size, 4))
|
||||
zh:write(number_to_bytestring(lfh.uncompressed_size, 4))
|
||||
zh:write(number_to_bytestring(lfh.file_name_length, 2))
|
||||
zh:write(number_to_bytestring(lfh.extra_field_length, 2))
|
||||
zh:write(number_to_bytestring(0, 2)) -- file comment length
|
||||
zh:write(number_to_bytestring(0, 2)) -- disk number start
|
||||
zh:write(number_to_bytestring(0, 2)) -- internal file attributes
|
||||
zh:write(number_to_bytestring(lfh.external_attr, 4)) -- external file attributes
|
||||
zh:write(number_to_bytestring(lfh.offset, 4)) -- relative offset of local header
|
||||
zh:write(lfh.file_name)
|
||||
size_of_central_directory = size_of_central_directory + 46 + lfh.file_name_length
|
||||
end
|
||||
|
||||
-- End of central directory record
|
||||
zh:write(number_to_bytestring(0x06054b50, 4)) -- signature
|
||||
zh:write(number_to_bytestring(0, 2)) -- number of this disk
|
||||
zh:write(number_to_bytestring(0, 2)) -- number of disk with start of central directory
|
||||
zh:write(number_to_bytestring(#self.files, 2)) -- total number of entries in the central dir on this disk
|
||||
zh:write(number_to_bytestring(#self.files, 2)) -- total number of entries in the central dir
|
||||
zh:write(number_to_bytestring(size_of_central_directory, 4))
|
||||
zh:write(number_to_bytestring(central_directory_offset, 4))
|
||||
zh:write(number_to_bytestring(0, 2)) -- zip file comment length
|
||||
zh:close()
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
--- Return a zip handle open for writing.
|
||||
-- @param name filename of the zipfile to be created.
|
||||
-- @return a zip handle, or nil in case of error.
|
||||
function zip.new_zipwriter(name)
|
||||
|
||||
local zw = {}
|
||||
|
||||
zw.ziphandle = io.open(fs.absolute_name(name), "wb")
|
||||
if not zw.ziphandle then
|
||||
return nil
|
||||
end
|
||||
zw.files = {}
|
||||
zw.in_open_file = false
|
||||
|
||||
zw.add = zipwriter_add
|
||||
zw.close = zipwriter_close
|
||||
zw.open_new_file_in_zip = zipwriter_open_new_file_in_zip
|
||||
zw.write_file_in_zip = zipwriter_write_file_in_zip
|
||||
zw.close_file_in_zip = zipwriter_close_file_in_zip
|
||||
|
||||
return zw
|
||||
end
|
||||
|
||||
--- Compress files in a .zip archive.
|
||||
-- @param zipfile string: pathname of .zip archive to be created.
|
||||
-- @param ... Filenames to be stored in the archive are given as
|
||||
-- additional arguments.
|
||||
-- @return boolean or (boolean, string): true on success,
|
||||
-- false and an error message on failure.
|
||||
function zip.zip(zipfile, ...)
|
||||
local zw = zip.new_zipwriter(zipfile)
|
||||
if not zw then
|
||||
return nil, "error opening "..zipfile
|
||||
end
|
||||
|
||||
local ok, err
|
||||
for _, file in pairs({...}) do
|
||||
if fs.is_dir(file) then
|
||||
for _, entry in pairs(fs.find(file)) do
|
||||
local fullname = dir.path(file, entry)
|
||||
if fs.is_file(fullname) then
|
||||
ok, err = zw:add(fullname)
|
||||
if not ok then break end
|
||||
end
|
||||
end
|
||||
else
|
||||
ok, err = zw:add(file)
|
||||
if not ok then break end
|
||||
end
|
||||
end
|
||||
|
||||
ok = zw:close()
|
||||
if not ok then
|
||||
return false, "error closing "..zipfile
|
||||
end
|
||||
return ok, err
|
||||
end
|
||||
|
||||
|
||||
return zip
|
||||
Reference in New Issue
Block a user