aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorV1K1NGbg <victor@ilchev.com>2024-08-22 17:48:59 -0300
committerHisham Muhammad <hisham@gobolinux.org>2024-10-21 13:30:51 -0300
commit55450f45af0eb730a679aa2c016a5282c22882eb (patch)
tree436cac6a03005b4cc5c7ad76d966e044072d0b87 /src
parentbb80ad3ceaaaabc57d7a732a09d9052d0b26d72e (diff)
downloadluarocks-55450f45af0eb730a679aa2c016a5282c22882eb.tar.gz
luarocks-55450f45af0eb730a679aa2c016a5282c22882eb.tar.bz2
luarocks-55450f45af0eb730a679aa2c016a5282c22882eb.zip
Teal: convert luarocks.tools.patch
Diffstat (limited to 'src')
-rw-r--r--src/luarocks/tools/patch.tl (renamed from src/luarocks/tools/patch.lua)233
1 files changed, 132 insertions, 101 deletions
diff --git a/src/luarocks/tools/patch.lua b/src/luarocks/tools/patch.tl
index 10654e06..95715176 100644
--- a/src/luarocks/tools/patch.lua
+++ b/src/luarocks/tools/patch.tl
@@ -8,59 +8,88 @@
8-- Project home: http://code.google.com/p/python-patch/ . 8-- Project home: http://code.google.com/p/python-patch/ .
9-- Version 0.1 9-- Version 0.1
10 10
11local patch = {} 11local record patch
12 record Lineends
13 lf: integer
14 crlf: integer
15 cr: integer
16 end
17 record Hunk
18 startsrc: integer
19 linessrc: integer
20 starttgt: integer
21 linestgt: integer
22 invalid: boolean
23 text: {string}
24 end
25 record File --!
26 at: integer
27 str: string
28 len: integer
29 eof: boolean
30 read: function(File, integer): string
31 close: function(File): boolean
32 end
33 record Files
34 source: {string} --!
35 target: {string}
36 epoch: {boolean}
37 hunks: {{Hunk}}
38 fileends: {Lineends}
39 hunkends: {Lineends}
40 end
41end
12 42
13local fs = require("luarocks.fs") 43local fs = require("luarocks.fs")
14 44
15local io = io 45local type Lineends = patch.Lineends
16local os = os 46local type Hunk = patch.Hunk
17local string = string 47local type Files = patch.Files
18local table = table 48local type File = patch.File
19local format = string.format
20 49
21-- logging 50-- logging
22local debugmode = false 51local debugmode = false
23local function debug(_) end 52local function debug(_: string) end --!
24local function info(_) end 53local function info(_: string) end
25local function warning(s) io.stderr:write(s .. '\n') end 54local function warning(s: string) io.stderr:write(s .. '\n') end
26 55
27-- Returns boolean whether string s2 starts with string s. 56-- Returns boolean whether string s2 starts with string s.
28local function startswith(s, s2) 57local function startswith(s: string, s2: string): boolean
29 return s:sub(1, #s2) == s2 58 return s:sub(1, #s2) == s2
30end 59end
31 60
32-- Returns boolean whether string s2 ends with string s. 61-- Returns boolean whether string s2 ends with string s.
33local function endswith(s, s2) 62local function endswith(s: string, s2: string): boolean
34 return #s >= #s2 and s:sub(#s-#s2+1) == s2 63 return #s >= #s2 and s:sub(#s-#s2+1) == s2
35end 64end
36 65
37-- Returns string s after filtering out any new-line characters from end. 66-- Returns string s after filtering out any new-line characters from end.
38local function endlstrip(s) 67local function endlstrip(s: string): string, integer
39 return s:gsub('[\r\n]+$', '') 68 return s:gsub('[\r\n]+$', '')
40end 69end
41 70
42-- Returns shallow copy of table t. 71-- Returns shallow copy of table t.
43local function table_copy(t) 72local function table_copy<K, V>(t: {K: V}): {K: V}
44 local t2 = {} 73 local t2 = {}
45 for k,v in pairs(t) do t2[k] = v end 74 for k,v in pairs(t) do t2[k] = v end
46 return t2 75 return t2
47end 76end
48 77
49local function exists(filename) 78local function exists(filename: string): boolean
50 local fh = io.open(filename) 79 local fh = io.open(filename)
51 local result = fh ~= nil 80 local result = fh ~= nil
52 if fh then fh:close() end 81 if fh then fh:close() end
53 return result 82 return result
54end 83end
55local function isfile() return true end --FIX? 84local function isfile(): boolean return true end --FIX? --!
56 85
57local function string_as_file(s) 86local function string_as_file(s: string): File --!
58 return { 87 return {
59 at = 0, 88 at = 0,
60 str = s, 89 str = s,
61 len = #s, 90 len = #s,
62 eof = false, 91 eof = false,
63 read = function(self, n) 92 read = function(self: File, n: integer): string
64 if self.eof then return nil end 93 if self.eof then return nil end
65 local chunk = self.str:sub(self.at, self.at + n - 1) 94 local chunk = self.str:sub(self.at, self.at + n - 1)
66 self.at = self.at + n 95 self.at = self.at + n
@@ -69,10 +98,10 @@ local function string_as_file(s)
69 end 98 end
70 return chunk 99 return chunk
71 end, 100 end,
72 close = function(self) 101 close = function(self: File): boolean
73 self.eof = true 102 self.eof = true
74 end, 103 end,
75 } 104 } as File
76end 105end
77 106
78-- 107--
@@ -84,12 +113,12 @@ end
84-- in binary or ASCII mode. 113-- in binary or ASCII mode.
85-- (file_lines - version 20080913) 114-- (file_lines - version 20080913)
86-- 115--
87local function file_lines(f) 116local function file_lines(f: FILE): function(): string
88 local CHUNK_SIZE = 1024 117 local CHUNK_SIZE = 1024
89 local buffer = "" 118 local buffer = ""
90 local pos_beg = 1 119 local pos_beg = 1
91 return function() 120 return function(): string
92 local pos, chars 121 local pos, chars: string, string
93 while 1 do 122 while 1 do
94 pos, chars = buffer:match('()([\r\n].)', pos_beg) 123 pos, chars = buffer:match('()([\r\n].)', pos_beg)
95 if pos or not f then 124 if pos or not f then
@@ -97,27 +126,28 @@ local function file_lines(f)
97 elseif f then 126 elseif f then
98 local chunk = f:read(CHUNK_SIZE) 127 local chunk = f:read(CHUNK_SIZE)
99 if chunk then 128 if chunk then
100 buffer = buffer:sub(pos_beg) .. chunk 129 buffer = buffer:sub(pos_beg) .. chunk --! funcy stuff with pos
101 pos_beg = 1 130 pos_beg = 1
102 else 131 else
103 f = nil 132 f = nil
104 end 133 end
105 end 134 end
106 end 135 end
107 if not pos then 136 local posi = math.tointeger(pos)
108 pos = #buffer 137 if not posi then
138 posi = #buffer
109 elseif chars == '\r\n' then 139 elseif chars == '\r\n' then
110 pos = pos + 1 140 posi = posi + 1
111 end 141 end
112 local line = buffer:sub(pos_beg, pos) 142 local line = buffer:sub(pos_beg, posi)
113 pos_beg = pos + 1 143 pos_beg = posi + 1
114 if #line > 0 then 144 if #line > 0 then
115 return line 145 return line
116 end 146 end
117 end 147 end
118end 148end
119 149
120local function match_linerange(line) 150local function match_linerange(line: string): string, string, string, string
121 local m1, m2, m3, m4 = line:match("^@@ %-(%d+),(%d+) %+(%d+),(%d+)") 151 local m1, m2, m3, m4 = line:match("^@@ %-(%d+),(%d+) %+(%d+),(%d+)")
122 if not m1 then m1, m3, m4 = line:match("^@@ %-(%d+) %+(%d+),(%d+)") end 152 if not m1 then m1, m3, m4 = line:match("^@@ %-(%d+) %+(%d+),(%d+)") end
123 if not m1 then m1, m2, m3 = line:match("^@@ %-(%d+),(%d+) %+(%d+)") end 153 if not m1 then m1, m2, m3 = line:match("^@@ %-(%d+),(%d+) %+(%d+)") end
@@ -125,11 +155,11 @@ local function match_linerange(line)
125 return m1, m2, m3, m4 155 return m1, m2, m3, m4
126end 156end
127 157
128local function match_epoch(str) 158local function match_epoch(str: string): string
129 return str:match("[^0-9]1969[^0-9]") or str:match("[^0-9]1970[^0-9]") 159 return str:match("[^0-9]1969[^0-9]") or str:match("[^0-9]1970[^0-9]")
130end 160end
131 161
132function patch.read_patch(filename, data) 162function patch.read_patch(filename: string, data: string): Files, boolean
133 -- define possible file regions that will direct the parser flow 163 -- define possible file regions that will direct the parser flow
134 local state = 'header' 164 local state = 'header'
135 -- 'header' - comments before the patch body 165 -- 'header' - comments before the patch body
@@ -139,26 +169,26 @@ function patch.read_patch(filename, data)
139 -- 'hunkskip' - skipping invalid hunk mode 169 -- 'hunkskip' - skipping invalid hunk mode
140 170
141 local all_ok = true 171 local all_ok = true
142 local lineends = {lf=0, crlf=0, cr=0} 172 local lineends: Lineends = {lf=0, crlf=0, cr=0}
143 local files = {source={}, target={}, epoch={}, hunks={}, fileends={}, hunkends={}} 173 local files: Files = {source={}, target={}, epoch={}, hunks: {{Hunk}}={}, fileends={}, hunkends: {Lineends}={}}
144 local nextfileno = 0 174 local nextfileno = 0
145 local nexthunkno = 0 --: even if index starts with 0 user messages 175 local nexthunkno = 0 --: even if index starts with 0 user messages
146 -- number hunks from 1 176 -- number hunks from 1
147 177
148 -- hunkinfo holds parsed values, hunkactual - calculated 178 -- hunkinfo holds parsed values, hunkactual - calculated
149 local hunkinfo = { 179 local hunkinfo: Hunk = {
150 startsrc=nil, linessrc=nil, starttgt=nil, linestgt=nil, 180 startsrc=nil, linessrc=nil, starttgt=nil, linestgt=nil,
151 invalid=false, text={} 181 invalid=false, text={}
152 } 182 }
153 local hunkactual = {linessrc=nil, linestgt=nil} 183 local hunkactual: Hunk = {linessrc=nil, linestgt=nil}
154 184
155 info(format("reading patch %s", filename)) 185 info(string.format("reading patch %s", filename)) --!
156 186
157 local fp 187 local fp: FILE
158 if data then 188 if data then
159 fp = string_as_file(data) 189 fp = string_as_file(data) as FILE --! cast
160 else 190 else
161 fp = filename == '-' and io.stdin or assert(io.open(filename, "rb")) 191 fp = filename == '-' and io.stdin or assert(io.open(filename, "rb") as (FILE, string)) --! use of cast
162 end 192 end
163 local lineno = 0 193 local lineno = 0
164 194
@@ -202,10 +232,10 @@ function patch.read_patch(filename, data)
202 table.insert(hunkinfo.text, line) 232 table.insert(hunkinfo.text, line)
203 -- todo: handle \ No newline cases 233 -- todo: handle \ No newline cases
204 else 234 else
205 warning(format("invalid hunk no.%d at %d for target file %s", 235 warning(string.format("invalid hunk no.%d at %d for target file %s",
206 nexthunkno, lineno, files.target[nextfileno])) 236 nexthunkno, lineno, files.target[nextfileno]))
207 -- add hunk status node 237 -- add hunk status node
208 table.insert(files.hunks[nextfileno], table_copy(hunkinfo)) 238 table.insert(files.hunks[nextfileno], table_copy(hunkinfo as {any: any}) as Hunk)
209 files.hunks[nextfileno][nexthunkno].invalid = true 239 files.hunks[nextfileno][nexthunkno].invalid = true
210 all_ok = false 240 all_ok = false
211 state = 'hunkskip' 241 state = 'hunkskip'
@@ -215,16 +245,16 @@ function patch.read_patch(filename, data)
215 if hunkactual.linessrc > hunkinfo.linessrc or 245 if hunkactual.linessrc > hunkinfo.linessrc or
216 hunkactual.linestgt > hunkinfo.linestgt 246 hunkactual.linestgt > hunkinfo.linestgt
217 then 247 then
218 warning(format("extra hunk no.%d lines at %d for target %s", 248 warning(string.format("extra hunk no.%d lines at %d for target %s",
219 nexthunkno, lineno, files.target[nextfileno])) 249 nexthunkno, lineno, files.target[nextfileno]))
220 -- add hunk status node 250 -- add hunk status node
221 table.insert(files.hunks[nextfileno], table_copy(hunkinfo)) 251 table.insert(files.hunks[nextfileno], table_copy(hunkinfo as {any: any}) as Hunk)
222 files.hunks[nextfileno][nexthunkno].invalid = true 252 files.hunks[nextfileno][nexthunkno].invalid = true
223 state = 'hunkskip' 253 state = 'hunkskip'
224 elseif hunkinfo.linessrc == hunkactual.linessrc and 254 elseif hunkinfo.linessrc == hunkactual.linessrc and
225 hunkinfo.linestgt == hunkactual.linestgt 255 hunkinfo.linestgt == hunkactual.linestgt
226 then 256 then
227 table.insert(files.hunks[nextfileno], table_copy(hunkinfo)) 257 table.insert(files.hunks[nextfileno], table_copy(hunkinfo as {any: any}) as Hunk)
228 state = 'hunkskip' 258 state = 'hunkskip'
229 259
230 -- detect mixed window/unix line ends 260 -- detect mixed window/unix line ends
@@ -232,7 +262,7 @@ function patch.read_patch(filename, data)
232 if (ends.cr~=0 and 1 or 0) + (ends.crlf~=0 and 1 or 0) + 262 if (ends.cr~=0 and 1 or 0) + (ends.crlf~=0 and 1 or 0) +
233 (ends.lf~=0 and 1 or 0) > 1 263 (ends.lf~=0 and 1 or 0) > 1
234 then 264 then
235 warning(format("inconsistent line ends in patch hunks for %s", 265 warning(string.format("inconsistent line ends in patch hunks for %s",
236 files.source[nextfileno])) 266 files.source[nextfileno]))
237 end 267 end
238 end 268 end
@@ -245,18 +275,18 @@ function patch.read_patch(filename, data)
245 elseif startswith(line, "--- ") then 275 elseif startswith(line, "--- ") then
246 state = 'filenames' 276 state = 'filenames'
247 if debugmode and #files.source > 0 then 277 if debugmode and #files.source > 0 then
248 debug(format("- %2d hunks for %s", #files.hunks[nextfileno], 278 debug(string.format("- %2d hunks for %s", #files.hunks[nextfileno],
249 files.source[nextfileno])) 279 files.source[nextfileno]))
250 end 280 end
251 end 281 end
252 -- state is 'hunkskip', 'hunkhead', or 'filenames' 282 -- state is 'hunkskip', 'hunkhead', or 'filenames'
253 end 283 end
254 local advance 284 local advance: boolean
255 if state == 'filenames' then 285 if state == 'filenames' then
256 if startswith(line, "--- ") then 286 if startswith(line, "--- ") then
257 if files.source[nextfileno] then 287 if files.source[nextfileno] then
258 all_ok = false 288 all_ok = false
259 warning(format("skipping invalid patch for %s", 289 warning(string.format("skipping invalid patch for %s",
260 files.source[nextfileno+1])) 290 files.source[nextfileno+1]))
261 table.remove(files.source, nextfileno+1) 291 table.remove(files.source, nextfileno+1)
262 -- double source filename line is encountered 292 -- double source filename line is encountered
@@ -268,7 +298,7 @@ function patch.read_patch(filename, data)
268 local match, rest = line:match("^%-%-%- ([^ \t\r\n]+)(.*)") 298 local match, rest = line:match("^%-%-%- ([^ \t\r\n]+)(.*)")
269 if not match then 299 if not match then
270 all_ok = false 300 all_ok = false
271 warning(format("skipping invalid filename at line %d", lineno+1)) 301 warning(string.format("skipping invalid filename at line %d", lineno+1))
272 state = 'header' 302 state = 'header'
273 else 303 else
274 if match_epoch(rest) then 304 if match_epoch(rest) then
@@ -279,7 +309,7 @@ function patch.read_patch(filename, data)
279 elseif not startswith(line, "+++ ") then 309 elseif not startswith(line, "+++ ") then
280 if files.source[nextfileno] then 310 if files.source[nextfileno] then
281 all_ok = false 311 all_ok = false
282 warning(format("skipping invalid patch with no target for %s", 312 warning(string.format("skipping invalid patch with no target for %s",
283 files.source[nextfileno+1])) 313 files.source[nextfileno+1]))
284 table.remove(files.source, nextfileno+1) 314 table.remove(files.source, nextfileno+1)
285 else 315 else
@@ -290,7 +320,7 @@ function patch.read_patch(filename, data)
290 else 320 else
291 if files.target[nextfileno] then 321 if files.target[nextfileno] then
292 all_ok = false 322 all_ok = false
293 warning(format("skipping invalid patch - double target at line %d", 323 warning(string.format("skipping invalid patch - double target at line %d",
294 lineno+1)) 324 lineno+1))
295 table.remove(files.source, nextfileno+1) 325 table.remove(files.source, nextfileno+1)
296 table.remove(files.target, nextfileno+1) 326 table.remove(files.target, nextfileno+1)
@@ -306,7 +336,7 @@ function patch.read_patch(filename, data)
306 local match, rest = line:match(re_filename) 336 local match, rest = line:match(re_filename)
307 if not match then 337 if not match then
308 all_ok = false 338 all_ok = false
309 warning(format( 339 warning(string.format(
310 "skipping invalid patch - no target filename at line %d", 340 "skipping invalid patch - no target filename at line %d",
311 lineno+1)) 341 lineno+1))
312 state = 'header' 342 state = 'header'
@@ -318,8 +348,8 @@ function patch.read_patch(filename, data)
318 end 348 end
319 nexthunkno = 0 349 nexthunkno = 0
320 table.insert(files.hunks, {}) 350 table.insert(files.hunks, {})
321 table.insert(files.hunkends, table_copy(lineends)) 351 table.insert(files.hunkends, table_copy(lineends as {any: any}) as Lineends)
322 table.insert(files.fileends, table_copy(lineends)) 352 table.insert(files.fileends, table_copy(lineends as {any: any}) as Lineends)
323 state = 'hunkhead' 353 state = 'hunkhead'
324 advance = true 354 advance = true
325 end 355 end
@@ -330,17 +360,17 @@ function patch.read_patch(filename, data)
330 if not advance and state == 'hunkhead' then 360 if not advance and state == 'hunkhead' then
331 local m1, m2, m3, m4 = match_linerange(line) 361 local m1, m2, m3, m4 = match_linerange(line)
332 if not m1 then 362 if not m1 then
333 if not files.hunks[nextfileno-1] then 363 if not files.hunks[nextfileno - 1] then
334 all_ok = false 364 all_ok = false
335 warning(format("skipping invalid patch with no hunks for file %s", 365 warning(string.format("skipping invalid patch with no hunks for file %s",
336 files.target[nextfileno])) 366 files.target[nextfileno]))
337 end 367 end
338 state = 'header' 368 state = 'header'
339 else 369 else
340 hunkinfo.startsrc = tonumber(m1) 370 hunkinfo.startsrc = math.tointeger(m1)
341 hunkinfo.linessrc = tonumber(m2 or 1) 371 hunkinfo.linessrc = math.tointeger(m2) or 1
342 hunkinfo.starttgt = tonumber(m3) 372 hunkinfo.starttgt = math.tointeger(m3)
343 hunkinfo.linestgt = tonumber(m4 or 1) 373 hunkinfo.linestgt = math.tointeger(m4) or 1
344 hunkinfo.invalid = false 374 hunkinfo.invalid = false
345 hunkinfo.text = {} 375 hunkinfo.text = {}
346 376
@@ -354,24 +384,24 @@ function patch.read_patch(filename, data)
354 end 384 end
355 end 385 end
356 if state ~= 'hunkskip' then 386 if state ~= 'hunkskip' then
357 warning(format("patch file incomplete - %s", filename)) 387 warning(string.format("patch file incomplete - %s", filename))
358 all_ok = false 388 all_ok = false
359 -- os.exit(?) 389 -- os.exit(?)
360 else 390 else
361 -- duplicated message when an eof is reached 391 -- duplicated message when an eof is reached
362 if debugmode and #files.source > 0 then 392 if debugmode and #files.source > 0 then
363 debug(format("- %2d hunks for %s", #files.hunks[nextfileno], 393 debug(string.format("- %2d hunks for %s", #files.hunks[nextfileno],
364 files.source[nextfileno])) 394 files.source[nextfileno]))
365 end 395 end
366 end 396 end
367 397
368 local sum = 0; for _,hset in ipairs(files.hunks) do sum = sum + #hset end 398 local sum = 0; for _,hset in ipairs(files.hunks) do sum = sum + #hset end
369 info(format("total files: %d total hunks: %d", #files.source, sum)) 399 info(string.format("total files: %d total hunks: %d", #files.source, sum))
370 fp:close() 400 fp:close()
371 return files, all_ok 401 return files, all_ok
372end 402end
373 403
374local function find_hunk(file, h, hno) 404local function find_hunk(file: {string}, h: Hunk, hno: integer): boolean
375 for fuzz=0,2 do 405 for fuzz=0,2 do
376 local lineno = h.startsrc 406 local lineno = h.startsrc
377 for i=0,#file do 407 for i=0,#file do
@@ -381,7 +411,7 @@ local function find_hunk(file, h, hno)
381 if l > fuzz then 411 if l > fuzz then
382 -- todo: \ No newline at the end of file 412 -- todo: \ No newline at the end of file
383 if startswith(hline, " ") or startswith(hline, "-") then 413 if startswith(hline, " ") or startswith(hline, "-") then
384 local line = file[lineno] 414 local line = file[lineno] --! cast
385 lineno = lineno + 1 415 lineno = lineno + 1
386 if not line or #line == 0 then 416 if not line or #line == 0 then
387 found = false 417 found = false
@@ -397,7 +427,7 @@ local function find_hunk(file, h, hno)
397 if found then 427 if found then
398 local offset = location - h.startsrc - fuzz 428 local offset = location - h.startsrc - fuzz
399 if offset ~= 0 then 429 if offset ~= 0 then
400 warning(format("Hunk %d found at offset %d%s...", hno, offset, fuzz == 0 and "" or format(" (fuzz %d)", fuzz))) 430 warning(string.format("Hunk %d found at offset %d%s...", hno, offset, fuzz == 0 and "" or string.format(" (fuzz %d)", fuzz)))
401 end 431 end
402 h.startsrc = location 432 h.startsrc = location
403 h.starttgt = h.starttgt + offset 433 h.starttgt = h.starttgt + offset
@@ -413,8 +443,8 @@ local function find_hunk(file, h, hno)
413 return false 443 return false
414end 444end
415 445
416local function load_file(filename) 446local function load_file(filename: string): {string}
417 local fp = assert(io.open(filename)) 447 local fp = assert(io.open(filename) as (FILE, string)) --! cast
418 local file = {} 448 local file = {}
419 local readline = file_lines(fp) 449 local readline = file_lines(fp)
420 while true do 450 while true do
@@ -426,15 +456,15 @@ local function load_file(filename)
426 return file 456 return file
427end 457end
428 458
429local function find_hunks(file, hunks) 459local function find_hunks(file: {string}, hunks: {Hunk})
430 for hno, h in ipairs(hunks) do 460 for hno, h in ipairs(hunks) do
431 find_hunk(file, h, hno) 461 find_hunk(file, h, hno)
432 end 462 end
433end 463end
434 464
435local function check_patched(file, hunks) 465local function check_patched(file: {string}, hunks: {Hunk}): boolean
436 local lineno = 1 466 local lineno: integer = 1
437 local ok, err = pcall(function() 467 local _, err: boolean, string = pcall(function()
438 if #file == 0 then 468 if #file == 0 then
439 error('nomatch', 0) 469 error('nomatch', 0)
440 end 470 end
@@ -443,7 +473,7 @@ local function check_patched(file, hunks)
443 if #file < h.starttgt then 473 if #file < h.starttgt then
444 error('nomatch', 0) 474 error('nomatch', 0)
445 end 475 end
446 lineno = h.starttgt 476 lineno = h.starttgt as integer --! cast
447 for _, hline in ipairs(h.text) do 477 for _, hline in ipairs(h.text) do
448 -- todo: \ No newline at the end of file 478 -- todo: \ No newline at the end of file
449 if not startswith(hline, "-") and not startswith(hline, "\\") then 479 if not startswith(hline, "-") and not startswith(hline, "\\") then
@@ -453,7 +483,7 @@ local function check_patched(file, hunks)
453 error('nomatch', 0) 483 error('nomatch', 0)
454 end 484 end
455 if endlstrip(line) ~= endlstrip(hline:sub(2)) then 485 if endlstrip(line) ~= endlstrip(hline:sub(2)) then
456 warning(format("file is not patched - failed hunk: %d", hno)) 486 warning(string.format("file is not patched - failed hunk: %d", hno))
457 error('nomatch', 0) 487 error('nomatch', 0)
458 end 488 end
459 end 489 end
@@ -464,9 +494,9 @@ local function check_patched(file, hunks)
464 return err ~= 'nomatch' 494 return err ~= 'nomatch'
465end 495end
466 496
467local function patch_hunks(srcname, tgtname, hunks) 497local function patch_hunks(srcname: string, tgtname: string, hunks: {Hunk}): boolean
468 local src = assert(io.open(srcname, "rb")) 498 local src = assert(io.open(srcname, "rb") as (FILE, string)) --! cast
469 local tgt = assert(io.open(tgtname, "wb")) 499 local tgt = assert(io.open(tgtname, "wb") as (FILE, string)) --! cast
470 500
471 local src_readline = file_lines(src) 501 local src_readline = file_lines(src)
472 502
@@ -479,7 +509,7 @@ local function patch_hunks(srcname, tgtname, hunks)
479 local srclineno = 1 509 local srclineno = 1
480 local lineends = {['\n']=0, ['\r\n']=0, ['\r']=0} 510 local lineends = {['\n']=0, ['\r\n']=0, ['\r']=0}
481 for hno, h in ipairs(hunks) do 511 for hno, h in ipairs(hunks) do
482 debug(format("processing hunk %d for file %s", hno, tgtname)) 512 debug(string.format("processing hunk %d for file %s", hno, tgtname))
483 -- skip to line just before hunk starts 513 -- skip to line just before hunk starts
484 while srclineno < h.startsrc do 514 while srclineno < h.startsrc do
485 local line = src_readline() 515 local line = src_readline()
@@ -508,10 +538,10 @@ local function patch_hunks(srcname, tgtname, hunks)
508 local line2write = hline:sub(2) 538 local line2write = hline:sub(2)
509 -- detect if line ends are consistent in source file 539 -- detect if line ends are consistent in source file
510 local sum = 0 540 local sum = 0
511 for _,v in pairs(lineends) do if v > 0 then sum=sum+1 end end 541 for _,v in pairs(lineends as {string:any}) do if v as integer > 0 then sum=sum+1 end end --! unchecked
512 if sum == 1 then 542 if sum == 1 then
513 local newline 543 local newline: string
514 for k,v in pairs(lineends) do if v ~= 0 then newline = k end end 544 for k,v in pairs(lineends as {string:any}) do if v ~= 0 then newline = k end end
515 tgt:write(endlstrip(line2write) .. newline) 545 tgt:write(endlstrip(line2write) .. newline)
516 else -- newlines are mixed or unknown 546 else -- newlines are mixed or unknown
517 tgt:write(line2write) 547 tgt:write(line2write)
@@ -527,7 +557,7 @@ local function patch_hunks(srcname, tgtname, hunks)
527 return true 557 return true
528end 558end
529 559
530local function strip_dirs(filename, strip) 560local function strip_dirs(filename: string, strip: integer): string
531 if strip == nil then return filename end 561 if strip == nil then return filename end
532 for _=1,strip do 562 for _=1,strip do
533 filename=filename:gsub("^[^/]*/", "") 563 filename=filename:gsub("^[^/]*/", "")
@@ -535,7 +565,7 @@ local function strip_dirs(filename, strip)
535 return filename 565 return filename
536end 566end
537 567
538local function write_new_file(filename, hunk) 568local function write_new_file(filename: string, hunk: Hunk): boolean, string
539 local fh = io.open(filename, "wb") 569 local fh = io.open(filename, "wb")
540 if not fh then return false end 570 if not fh then return false end
541 for _, hline in ipairs(hunk.text) do 571 for _, hline in ipairs(hunk.text) do
@@ -549,12 +579,12 @@ local function write_new_file(filename, hunk)
549 return true 579 return true
550end 580end
551 581
552local function patch_file(source, target, epoch, hunks, strip, create_delete) 582local function patch_file(source: string, target: string, epoch: boolean, hunks: {Hunk}, strip: integer, create_delete: boolean): boolean, string
553 local create_file = false 583 local create_file = false
554 if create_delete then 584 if create_delete then
555 local is_src_epoch = epoch and #hunks == 1 and hunks[1].startsrc == 0 and hunks[1].linessrc == 0 585 local is_src_epoch = epoch and #hunks == 1 and hunks[1].startsrc == 0 and hunks[1].linessrc == 0
556 if is_src_epoch or source == "/dev/null" then 586 if is_src_epoch or source == "/dev/null" then
557 info(format("will create %s", target)) 587 info(string.format("will create %s", target))
558 create_file = true 588 create_file = true
559 end 589 end
560 end 590 end
@@ -567,13 +597,14 @@ local function patch_file(source, target, epoch, hunks, strip, create_delete)
567 f2patch = strip_dirs(target, strip) 597 f2patch = strip_dirs(target, strip)
568 f2patch = fs.absolute_name(f2patch) 598 f2patch = fs.absolute_name(f2patch)
569 if not exists(f2patch) then --FIX:if f2patch nil 599 if not exists(f2patch) then --FIX:if f2patch nil
570 warning(format("source/target file does not exist\n--- %s\n+++ %s", 600 warning(string.format("source/target file does not exist\n--- %s\n+++ %s",
571 source, f2patch)) 601 source, f2patch))
572 return false 602 return false
573 end 603 end
574 end 604 end
575 if not isfile(f2patch) then 605 -- if not isfile(f2patch) then --!
576 warning(format("not a file - %s", f2patch)) 606 if not isfile() then --!
607 warning(string.format("not a file - %s", f2patch))
577 return false 608 return false
578 end 609 end
579 610
@@ -586,7 +617,7 @@ local function patch_file(source, target, epoch, hunks, strip, create_delete)
586 local hunkfind = {} 617 local hunkfind = {}
587 local validhunks = 0 618 local validhunks = 0
588 local canpatch = false 619 local canpatch = false
589 local hunklineno 620 local hunklineno: integer
590 if not file then 621 if not file then
591 return nil, "failed reading file " .. source 622 return nil, "failed reading file " .. source
592 end 623 end
@@ -597,14 +628,14 @@ local function patch_file(source, target, epoch, hunks, strip, create_delete)
597 if not ok then 628 if not ok then
598 return false 629 return false
599 end 630 end
600 info(format("successfully removed %s", source)) 631 info(string.format("successfully removed %s", source))
601 return true 632 return true
602 end 633 end
603 end 634 end
604 635
605 find_hunks(file, hunks) 636 find_hunks(file, hunks)
606 637
607 local function process_line(line, lineno) 638 local function process_line(line: string, lineno: integer): boolean
608 if not hunk or lineno < hunk.startsrc then 639 if not hunk or lineno < hunk.startsrc then
609 return false 640 return false
610 end 641 end
@@ -624,7 +655,7 @@ local function patch_file(source, target, epoch, hunks, strip, create_delete)
624 if endlstrip(line) == hunkfind[hunklineno] then 655 if endlstrip(line) == hunkfind[hunklineno] then
625 hunklineno = hunklineno + 1 656 hunklineno = hunklineno + 1
626 else 657 else
627 debug(format("hunk no.%d doesn't match source file %s", 658 debug(string.format("hunk no.%d doesn't match source file %s",
628 hunkno, source)) 659 hunkno, source))
629 -- file may be already patched, but check other hunks anyway 660 -- file may be already patched, but check other hunks anyway
630 hunkno = hunkno + 1 661 hunkno = hunkno + 1
@@ -638,7 +669,7 @@ local function patch_file(source, target, epoch, hunks, strip, create_delete)
638 end 669 end
639 -- check if processed line is the last line 670 -- check if processed line is the last line
640 if lineno == hunk.startsrc + #hunkfind - 1 then 671 if lineno == hunk.startsrc + #hunkfind - 1 then
641 debug(format("file %s hunk no.%d -- is ready to be patched", 672 debug(string.format("file %s hunk no.%d -- is ready to be patched",
642 source, hunkno)) 673 source, hunkno))
643 hunkno = hunkno + 1 674 hunkno = hunkno + 1
644 validhunks = validhunks + 1 675 validhunks = validhunks + 1
@@ -664,16 +695,16 @@ local function patch_file(source, target, epoch, hunks, strip, create_delete)
664 end 695 end
665 if not done then 696 if not done then
666 if hunkno <= #hunks and not create_file then 697 if hunkno <= #hunks and not create_file then
667 warning(format("premature end of source file %s at hunk %d", 698 warning(string.format("premature end of source file %s at hunk %d",
668 source, hunkno)) 699 source, hunkno))
669 return false 700 return false
670 end 701 end
671 end 702 end
672 if validhunks < #hunks then 703 if validhunks < #hunks then
673 if check_patched(file, hunks) then 704 if check_patched(file, hunks) then
674 warning(format("already patched %s", source)) 705 warning(string.format("already patched %s", source))
675 elseif not create_file then 706 elseif not create_file then
676 warning(format("source file is different - %s", source)) 707 warning(string.format("source file is different - %s", source))
677 return false 708 return false
678 end 709 end
679 end 710 end
@@ -682,29 +713,29 @@ local function patch_file(source, target, epoch, hunks, strip, create_delete)
682 end 713 end
683 local backupname = source .. ".orig" 714 local backupname = source .. ".orig"
684 if exists(backupname) then 715 if exists(backupname) then
685 warning(format("can't backup original file to %s - aborting", 716 warning(string.format("can't backup original file to %s - aborting",
686 backupname)) 717 backupname))
687 return false 718 return false
688 end 719 end
689 local ok = os.rename(source, backupname) 720 local ok = os.rename(source, backupname)
690 if not ok then 721 if not ok then
691 warning(format("failed backing up %s when patching", source)) 722 warning(string.format("failed backing up %s when patching", source))
692 return false 723 return false
693 end 724 end
694 patch_hunks(backupname, source, hunks) 725 patch_hunks(backupname, source, hunks)
695 info(format("successfully patched %s", source)) 726 info(string.format("successfully patched %s", source))
696 os.remove(backupname) 727 os.remove(backupname)
697 return true 728 return true
698end 729end
699 730
700function patch.apply_patch(the_patch, strip, create_delete) 731function patch.apply_patch(the_patch: Files, strip: integer, create_delete: boolean): boolean
701 local all_ok = true 732 local all_ok = true
702 local total = #the_patch.source 733 local total = #the_patch.source
703 for fileno, source in ipairs(the_patch.source) do 734 for fileno, source in ipairs(the_patch.source) do
704 local target = the_patch.target[fileno] 735 local target = the_patch.target[fileno]
705 local hunks = the_patch.hunks[fileno] 736 local hunks = the_patch.hunks[fileno]
706 local epoch = the_patch.epoch[fileno] 737 local epoch = the_patch.epoch[fileno]
707 info(format("processing %d/%d:\t %s", fileno, total, source)) 738 info(string.format("processing %d/%d:\t %s", fileno, total, source))
708 local ok = patch_file(source, target, epoch, hunks, strip, create_delete) 739 local ok = patch_file(source, target, epoch, hunks, strip, create_delete)
709 all_ok = all_ok and ok 740 all_ok = all_ok and ok
710 end 741 end