diff options
author | V1K1NGbg <victor@ilchev.com> | 2024-08-02 20:24:01 +0300 |
---|---|---|
committer | V1K1NGbg <victor@ilchev.com> | 2024-08-05 20:51:31 +0300 |
commit | e82c2fd083813c18a893bb19cbc013b6df44cafb (patch) | |
tree | d584dfd06a4a57d15fedc8973c63d0858c5c6db8 | |
parent | 5b189eea169bd69faa6705f6df68b8b132d2b0dc (diff) | |
download | luarocks-e82c2fd083813c18a893bb19cbc013b6df44cafb.tar.gz luarocks-e82c2fd083813c18a893bb19cbc013b6df44cafb.tar.bz2 luarocks-e82c2fd083813c18a893bb19cbc013b6df44cafb.zip |
move .d.tl to types folder
-rw-r--r-- | src/luarocks/tools/patch.tl | 718 | ||||
-rw-r--r-- | tlconfig.lua | 2 | ||||
-rw-r--r-- | types/ltn12.d.tl (renamed from src/ltn12.d.tl) | 0 | ||||
-rw-r--r-- | types/mimetypes.d.tl (renamed from src/mimetypes.d.tl) | 0 | ||||
-rw-r--r-- | types/socket.d.tl (renamed from src/socket.d.tl) | 0 | ||||
-rw-r--r-- | types/socket/http.d.tl (renamed from src/socket/http.d.tl) | 0 | ||||
-rw-r--r-- | types/ssl.d.tl (renamed from src/ssl.d.tl) | 0 | ||||
-rw-r--r-- | types/ssl/https.d.tl (renamed from src/ssl/https.d.tl) | 0 |
8 files changed, 719 insertions, 1 deletions
diff --git a/src/luarocks/tools/patch.tl b/src/luarocks/tools/patch.tl new file mode 100644 index 00000000..a3dbe0df --- /dev/null +++ b/src/luarocks/tools/patch.tl | |||
@@ -0,0 +1,718 @@ | |||
1 | --- Patch utility to apply unified diffs. | ||
2 | -- | ||
3 | -- http://lua-users.org/wiki/LuaPatch | ||
4 | -- | ||
5 | -- (c) 2008 David Manura, Licensed under the same terms as Lua (MIT license). | ||
6 | -- Code is heavily based on the Python-based patch.py version 8.06-1 | ||
7 | -- Copyright (c) 2008 rainforce.org, MIT License | ||
8 | -- Project home: http://code.google.com/p/python-patch/ . | ||
9 | -- Version 0.1 | ||
10 | |||
11 | local record patch | ||
12 | |||
13 | end | ||
14 | |||
15 | local fs = require("luarocks.fs") | ||
16 | local fun = require("luarocks.fun") | ||
17 | |||
18 | local io = io | ||
19 | local os = os | ||
20 | local string = string | ||
21 | local table = table | ||
22 | local format = string.format | ||
23 | |||
24 | -- logging | ||
25 | local debugmode = false | ||
26 | local function debug(_) end --! | ||
27 | local function info(_) end | ||
28 | local function warning(s: string) io.stderr:write(s .. '\n') end | ||
29 | |||
30 | -- Returns boolean whether string s2 starts with string s. | ||
31 | local function startswith(s: string, s2: string): boolean | ||
32 | return s:sub(1, #s2) == s2 | ||
33 | end | ||
34 | |||
35 | -- Returns boolean whether string s2 ends with string s. | ||
36 | local function endswith(s: string, s2: string): boolean | ||
37 | return #s >= #s2 and s:sub(#s-#s2+1) == s2 | ||
38 | end | ||
39 | |||
40 | -- Returns string s after filtering out any new-line characters from end. | ||
41 | local function endlstrip(s: string): string | ||
42 | return s:gsub('[\r\n]+$', '') | ||
43 | end | ||
44 | |||
45 | -- Returns shallow copy of table t. | ||
46 | local function table_copy<K, V>(t: {K: V}): {K: V} | ||
47 | local t2 = {} | ||
48 | for k,v in pairs(t) do t2[k] = v end | ||
49 | return t2 | ||
50 | end | ||
51 | |||
52 | local function exists(filename: string): boolean | ||
53 | local fh = io.open(filename) | ||
54 | local result = fh ~= nil | ||
55 | if fh then fh:close() end | ||
56 | return result | ||
57 | end | ||
58 | local function isfile(): boolean return true end --FIX? --! | ||
59 | |||
60 | local function string_as_file(s: string) --! | ||
61 | return { | ||
62 | at = 0, | ||
63 | str = s, | ||
64 | len = #s, | ||
65 | eof = false, | ||
66 | read = function(self, n) | ||
67 | if self.eof then return nil end | ||
68 | local chunk = self.str:sub(self.at, self.at + n - 1) | ||
69 | self.at = self.at + n | ||
70 | if self.at > self.len then | ||
71 | self.eof = true | ||
72 | end | ||
73 | return chunk | ||
74 | end, | ||
75 | close = function(self) | ||
76 | self.eof = true | ||
77 | end, | ||
78 | } | ||
79 | end | ||
80 | |||
81 | -- | ||
82 | -- file_lines(f) is similar to f:lines() for file f. | ||
83 | -- The main difference is that read_lines includes | ||
84 | -- new-line character sequences ("\n", "\r\n", "\r"), | ||
85 | -- if any, at the end of each line. Embedded "\0" are also handled. | ||
86 | -- Caution: The newline behavior can depend on whether f is opened | ||
87 | -- in binary or ASCII mode. | ||
88 | -- (file_lines - version 20080913) | ||
89 | -- | ||
90 | local function file_lines(f: FILE): function(): string | ||
91 | local CHUNK_SIZE = 1024 | ||
92 | local buffer = "" | ||
93 | local pos_beg = 1 | ||
94 | return function(): string | ||
95 | local pos, chars: number, number --? char represented as a number | ||
96 | while 1 do | ||
97 | pos, chars = buffer:match('()([\r\n].)', pos_beg) | ||
98 | if pos or not f then | ||
99 | break | ||
100 | elseif f then | ||
101 | local chunk = f:read(CHUNK_SIZE) | ||
102 | if chunk then | ||
103 | buffer = buffer:sub(pos_beg) .. chunk | ||
104 | pos_beg = 1 | ||
105 | else | ||
106 | f = nil | ||
107 | end | ||
108 | end | ||
109 | end | ||
110 | if not pos then | ||
111 | pos = #buffer | ||
112 | elseif chars == '\r\n' then | ||
113 | pos = pos + 1 | ||
114 | end | ||
115 | local line = buffer:sub(pos_beg, pos) | ||
116 | pos_beg = pos + 1 | ||
117 | if #line > 0 then | ||
118 | return line | ||
119 | end | ||
120 | end | ||
121 | end | ||
122 | |||
123 | local function match_linerange(line: string) | ||
124 | local m1, m2, m3, m4 = line:match("^@@ %-(%d+),(%d+) %+(%d+),(%d+)") | ||
125 | if not m1 then m1, m3, m4 = line:match("^@@ %-(%d+) %+(%d+),(%d+)") end | ||
126 | if not m1 then m1, m2, m3 = line:match("^@@ %-(%d+),(%d+) %+(%d+)") end | ||
127 | if not m1 then m1, m3 = line:match("^@@ %-(%d+) %+(%d+)") end | ||
128 | return m1, m2, m3, m4 | ||
129 | end | ||
130 | |||
131 | local function match_epoch(str) | ||
132 | return str:match("[^0-9]1969[^0-9]") or str:match("[^0-9]1970[^0-9]") | ||
133 | end | ||
134 | |||
135 | function patch.read_patch(filename, data) | ||
136 | -- define possible file regions that will direct the parser flow | ||
137 | local state = 'header' | ||
138 | -- 'header' - comments before the patch body | ||
139 | -- 'filenames' - lines starting with --- and +++ | ||
140 | -- 'hunkhead' - @@ -R +R @@ sequence | ||
141 | -- 'hunkbody' | ||
142 | -- 'hunkskip' - skipping invalid hunk mode | ||
143 | |||
144 | local all_ok = true | ||
145 | local lineends = {lf=0, crlf=0, cr=0} | ||
146 | local files = {source={}, target={}, epoch={}, hunks={}, fileends={}, hunkends={}} | ||
147 | local nextfileno = 0 | ||
148 | local nexthunkno = 0 --: even if index starts with 0 user messages | ||
149 | -- number hunks from 1 | ||
150 | |||
151 | -- hunkinfo holds parsed values, hunkactual - calculated | ||
152 | local hunkinfo = { | ||
153 | startsrc=nil, linessrc=nil, starttgt=nil, linestgt=nil, | ||
154 | invalid=false, text={} | ||
155 | } | ||
156 | local hunkactual = {linessrc=nil, linestgt=nil} | ||
157 | |||
158 | info(format("reading patch %s", filename)) | ||
159 | |||
160 | local fp | ||
161 | if data then | ||
162 | fp = string_as_file(data) | ||
163 | else | ||
164 | fp = filename == '-' and io.stdin or assert(io.open(filename, "rb")) | ||
165 | end | ||
166 | local lineno = 0 | ||
167 | |||
168 | for line in file_lines(fp) do | ||
169 | lineno = lineno + 1 | ||
170 | if state == 'header' then | ||
171 | if startswith(line, "--- ") then | ||
172 | state = 'filenames' | ||
173 | end | ||
174 | -- state is 'header' or 'filenames' | ||
175 | end | ||
176 | if state == 'hunkbody' then | ||
177 | -- skip hunkskip and hunkbody code until definition of hunkhead read | ||
178 | |||
179 | if line:match"^[\r\n]*$" then | ||
180 | -- prepend space to empty lines to interpret them as context properly | ||
181 | line = " " .. line | ||
182 | end | ||
183 | |||
184 | -- process line first | ||
185 | if line:match"^[- +\\]" then | ||
186 | -- gather stats about line endings | ||
187 | local he = files.hunkends[nextfileno] | ||
188 | if endswith(line, "\r\n") then | ||
189 | he.crlf = he.crlf + 1 | ||
190 | elseif endswith(line, "\n") then | ||
191 | he.lf = he.lf + 1 | ||
192 | elseif endswith(line, "\r") then | ||
193 | he.cr = he.cr + 1 | ||
194 | end | ||
195 | if startswith(line, "-") then | ||
196 | hunkactual.linessrc = hunkactual.linessrc + 1 | ||
197 | elseif startswith(line, "+") then | ||
198 | hunkactual.linestgt = hunkactual.linestgt + 1 | ||
199 | elseif startswith(line, "\\") then | ||
200 | -- nothing | ||
201 | else | ||
202 | hunkactual.linessrc = hunkactual.linessrc + 1 | ||
203 | hunkactual.linestgt = hunkactual.linestgt + 1 | ||
204 | end | ||
205 | table.insert(hunkinfo.text, line) | ||
206 | -- todo: handle \ No newline cases | ||
207 | else | ||
208 | warning(format("invalid hunk no.%d at %d for target file %s", | ||
209 | nexthunkno, lineno, files.target[nextfileno])) | ||
210 | -- add hunk status node | ||
211 | table.insert(files.hunks[nextfileno], table_copy(hunkinfo)) | ||
212 | files.hunks[nextfileno][nexthunkno].invalid = true | ||
213 | all_ok = false | ||
214 | state = 'hunkskip' | ||
215 | end | ||
216 | |||
217 | -- check exit conditions | ||
218 | if hunkactual.linessrc > hunkinfo.linessrc or | ||
219 | hunkactual.linestgt > hunkinfo.linestgt | ||
220 | then | ||
221 | warning(format("extra hunk no.%d lines at %d for target %s", | ||
222 | nexthunkno, lineno, files.target[nextfileno])) | ||
223 | -- add hunk status node | ||
224 | table.insert(files.hunks[nextfileno], table_copy(hunkinfo)) | ||
225 | files.hunks[nextfileno][nexthunkno].invalid = true | ||
226 | state = 'hunkskip' | ||
227 | elseif hunkinfo.linessrc == hunkactual.linessrc and | ||
228 | hunkinfo.linestgt == hunkactual.linestgt | ||
229 | then | ||
230 | table.insert(files.hunks[nextfileno], table_copy(hunkinfo)) | ||
231 | state = 'hunkskip' | ||
232 | |||
233 | -- detect mixed window/unix line ends | ||
234 | local ends = files.hunkends[nextfileno] | ||
235 | if (ends.cr~=0 and 1 or 0) + (ends.crlf~=0 and 1 or 0) + | ||
236 | (ends.lf~=0 and 1 or 0) > 1 | ||
237 | then | ||
238 | warning(format("inconsistent line ends in patch hunks for %s", | ||
239 | files.source[nextfileno])) | ||
240 | end | ||
241 | end | ||
242 | -- state is 'hunkbody' or 'hunkskip' | ||
243 | end | ||
244 | |||
245 | if state == 'hunkskip' then | ||
246 | if match_linerange(line) then | ||
247 | state = 'hunkhead' | ||
248 | elseif startswith(line, "--- ") then | ||
249 | state = 'filenames' | ||
250 | if debugmode and #files.source > 0 then | ||
251 | debug(format("- %2d hunks for %s", #files.hunks[nextfileno], | ||
252 | files.source[nextfileno])) | ||
253 | end | ||
254 | end | ||
255 | -- state is 'hunkskip', 'hunkhead', or 'filenames' | ||
256 | end | ||
257 | local advance | ||
258 | if state == 'filenames' then | ||
259 | if startswith(line, "--- ") then | ||
260 | if fun.contains(files.source, nextfileno) then | ||
261 | all_ok = false | ||
262 | warning(format("skipping invalid patch for %s", | ||
263 | files.source[nextfileno+1])) | ||
264 | table.remove(files.source, nextfileno+1) | ||
265 | -- double source filename line is encountered | ||
266 | -- attempt to restart from this second line | ||
267 | end | ||
268 | -- Accept a space as a terminator, like GNU patch does. | ||
269 | -- Breaks patches containing filenames with spaces... | ||
270 | -- FIXME Figure out what does GNU patch do in those cases. | ||
271 | local match, rest = line:match("^%-%-%- ([^ \t\r\n]+)(.*)") | ||
272 | if not match then | ||
273 | all_ok = false | ||
274 | warning(format("skipping invalid filename at line %d", lineno+1)) | ||
275 | state = 'header' | ||
276 | else | ||
277 | if match_epoch(rest) then | ||
278 | files.epoch[nextfileno + 1] = true | ||
279 | end | ||
280 | table.insert(files.source, match) | ||
281 | end | ||
282 | elseif not startswith(line, "+++ ") then | ||
283 | if fun.contains(files.source, nextfileno) then | ||
284 | all_ok = false | ||
285 | warning(format("skipping invalid patch with no target for %s", | ||
286 | files.source[nextfileno+1])) | ||
287 | table.remove(files.source, nextfileno+1) | ||
288 | else | ||
289 | -- this should be unreachable | ||
290 | warning("skipping invalid target patch") | ||
291 | end | ||
292 | state = 'header' | ||
293 | else | ||
294 | if fun.contains(files.target, nextfileno) then | ||
295 | all_ok = false | ||
296 | warning(format("skipping invalid patch - double target at line %d", | ||
297 | lineno+1)) | ||
298 | table.remove(files.source, nextfileno+1) | ||
299 | table.remove(files.target, nextfileno+1) | ||
300 | nextfileno = nextfileno - 1 | ||
301 | -- double target filename line is encountered | ||
302 | -- switch back to header state | ||
303 | state = 'header' | ||
304 | else | ||
305 | -- Accept a space as a terminator, like GNU patch does. | ||
306 | -- Breaks patches containing filenames with spaces... | ||
307 | -- FIXME Figure out what does GNU patch do in those cases. | ||
308 | local re_filename = "^%+%+%+ ([^ \t\r\n]+)(.*)$" | ||
309 | local match, rest = line:match(re_filename) | ||
310 | if not match then | ||
311 | all_ok = false | ||
312 | warning(format( | ||
313 | "skipping invalid patch - no target filename at line %d", | ||
314 | lineno+1)) | ||
315 | state = 'header' | ||
316 | else | ||
317 | table.insert(files.target, match) | ||
318 | nextfileno = nextfileno + 1 | ||
319 | if match_epoch(rest) then | ||
320 | files.epoch[nextfileno] = true | ||
321 | end | ||
322 | nexthunkno = 0 | ||
323 | table.insert(files.hunks, {}) | ||
324 | table.insert(files.hunkends, table_copy(lineends)) | ||
325 | table.insert(files.fileends, table_copy(lineends)) | ||
326 | state = 'hunkhead' | ||
327 | advance = true | ||
328 | end | ||
329 | end | ||
330 | end | ||
331 | -- state is 'filenames', 'header', or ('hunkhead' with advance) | ||
332 | end | ||
333 | if not advance and state == 'hunkhead' then | ||
334 | local m1, m2, m3, m4 = match_linerange(line) | ||
335 | if not m1 then | ||
336 | if not fun.contains(files.hunks, nextfileno-1) then | ||
337 | all_ok = false | ||
338 | warning(format("skipping invalid patch with no hunks for file %s", | ||
339 | files.target[nextfileno])) | ||
340 | end | ||
341 | state = 'header' | ||
342 | else | ||
343 | hunkinfo.startsrc = tonumber(m1) | ||
344 | hunkinfo.linessrc = tonumber(m2 or 1) | ||
345 | hunkinfo.starttgt = tonumber(m3) | ||
346 | hunkinfo.linestgt = tonumber(m4 or 1) | ||
347 | hunkinfo.invalid = false | ||
348 | hunkinfo.text = {} | ||
349 | |||
350 | hunkactual.linessrc = 0 | ||
351 | hunkactual.linestgt = 0 | ||
352 | |||
353 | state = 'hunkbody' | ||
354 | nexthunkno = nexthunkno + 1 | ||
355 | end | ||
356 | -- state is 'header' or 'hunkbody' | ||
357 | end | ||
358 | end | ||
359 | if state ~= 'hunkskip' then | ||
360 | warning(format("patch file incomplete - %s", filename)) | ||
361 | all_ok = false | ||
362 | -- os.exit(?) | ||
363 | else | ||
364 | -- duplicated message when an eof is reached | ||
365 | if debugmode and #files.source > 0 then | ||
366 | debug(format("- %2d hunks for %s", #files.hunks[nextfileno], | ||
367 | files.source[nextfileno])) | ||
368 | end | ||
369 | end | ||
370 | |||
371 | local sum = 0; for _,hset in ipairs(files.hunks) do sum = sum + #hset end | ||
372 | info(format("total files: %d total hunks: %d", #files.source, sum)) | ||
373 | fp:close() | ||
374 | return files, all_ok | ||
375 | end | ||
376 | |||
377 | local function find_hunk(file, h, hno) | ||
378 | for fuzz=0,2 do | ||
379 | local lineno = h.startsrc | ||
380 | for i=0,#file do | ||
381 | local found = true | ||
382 | local location = lineno | ||
383 | for l, hline in ipairs(h.text) do | ||
384 | if l > fuzz then | ||
385 | -- todo: \ No newline at the end of file | ||
386 | if startswith(hline, " ") or startswith(hline, "-") then | ||
387 | local line = file[lineno] | ||
388 | lineno = lineno + 1 | ||
389 | if not line or #line == 0 then | ||
390 | found = false | ||
391 | break | ||
392 | end | ||
393 | if endlstrip(line) ~= endlstrip(hline:sub(2)) then | ||
394 | found = false | ||
395 | break | ||
396 | end | ||
397 | end | ||
398 | end | ||
399 | end | ||
400 | if found then | ||
401 | local offset = location - h.startsrc - fuzz | ||
402 | if offset ~= 0 then | ||
403 | warning(format("Hunk %d found at offset %d%s...", hno, offset, fuzz == 0 and "" or format(" (fuzz %d)", fuzz))) | ||
404 | end | ||
405 | h.startsrc = location | ||
406 | h.starttgt = h.starttgt + offset | ||
407 | for _=1,fuzz do | ||
408 | table.remove(h.text, 1) | ||
409 | table.remove(h.text, #h.text) | ||
410 | end | ||
411 | return true | ||
412 | end | ||
413 | lineno = i | ||
414 | end | ||
415 | end | ||
416 | return false | ||
417 | end | ||
418 | |||
419 | local function load_file(filename) | ||
420 | local fp = assert(io.open(filename)) | ||
421 | local file = {} | ||
422 | local readline = file_lines(fp) | ||
423 | while true do | ||
424 | local line = readline() | ||
425 | if not line then break end | ||
426 | table.insert(file, line) | ||
427 | end | ||
428 | fp:close() | ||
429 | return file | ||
430 | end | ||
431 | |||
432 | local function find_hunks(file, hunks) | ||
433 | for hno, h in ipairs(hunks) do | ||
434 | find_hunk(file, h, hno) | ||
435 | end | ||
436 | end | ||
437 | |||
438 | local function check_patched(file, hunks) | ||
439 | local lineno = 1 | ||
440 | local ok, err = pcall(function() | ||
441 | if #file == 0 then | ||
442 | error('nomatch', 0) | ||
443 | end | ||
444 | for hno, h in ipairs(hunks) do | ||
445 | -- skip to line just before hunk starts | ||
446 | if #file < h.starttgt then | ||
447 | error('nomatch', 0) | ||
448 | end | ||
449 | lineno = h.starttgt | ||
450 | for _, hline in ipairs(h.text) do | ||
451 | -- todo: \ No newline at the end of file | ||
452 | if not startswith(hline, "-") and not startswith(hline, "\\") then | ||
453 | local line = file[lineno] | ||
454 | lineno = lineno + 1 | ||
455 | if #line == 0 then | ||
456 | error('nomatch', 0) | ||
457 | end | ||
458 | if endlstrip(line) ~= endlstrip(hline:sub(2)) then | ||
459 | warning(format("file is not patched - failed hunk: %d", hno)) | ||
460 | error('nomatch', 0) | ||
461 | end | ||
462 | end | ||
463 | end | ||
464 | end | ||
465 | end) | ||
466 | -- todo: display failed hunk, i.e. expected/found | ||
467 | return err ~= 'nomatch' | ||
468 | end | ||
469 | |||
470 | local function patch_hunks(srcname, tgtname, hunks) | ||
471 | local src = assert(io.open(srcname, "rb")) | ||
472 | local tgt = assert(io.open(tgtname, "wb")) | ||
473 | |||
474 | local src_readline = file_lines(src) | ||
475 | |||
476 | -- todo: detect linefeeds early - in apply_files routine | ||
477 | -- to handle cases when patch starts right from the first | ||
478 | -- line and no lines are processed. At the moment substituted | ||
479 | -- lineends may not be the same at the start and at the end | ||
480 | -- of patching. Also issue a warning about mixed lineends | ||
481 | |||
482 | local srclineno = 1 | ||
483 | local lineends = {['\n']=0, ['\r\n']=0, ['\r']=0} | ||
484 | for hno, h in ipairs(hunks) do | ||
485 | debug(format("processing hunk %d for file %s", hno, tgtname)) | ||
486 | -- skip to line just before hunk starts | ||
487 | while srclineno < h.startsrc do | ||
488 | local line = src_readline() | ||
489 | -- Python 'U' mode works only with text files | ||
490 | if endswith(line, "\r\n") then | ||
491 | lineends["\r\n"] = lineends["\r\n"] + 1 | ||
492 | elseif endswith(line, "\n") then | ||
493 | lineends["\n"] = lineends["\n"] + 1 | ||
494 | elseif endswith(line, "\r") then | ||
495 | lineends["\r"] = lineends["\r"] + 1 | ||
496 | end | ||
497 | tgt:write(line) | ||
498 | srclineno = srclineno + 1 | ||
499 | end | ||
500 | |||
501 | for _,hline in ipairs(h.text) do | ||
502 | -- todo: check \ No newline at the end of file | ||
503 | if startswith(hline, "-") or startswith(hline, "\\") then | ||
504 | src_readline() | ||
505 | srclineno = srclineno + 1 | ||
506 | else | ||
507 | if not startswith(hline, "+") then | ||
508 | src_readline() | ||
509 | srclineno = srclineno + 1 | ||
510 | end | ||
511 | local line2write = hline:sub(2) | ||
512 | -- detect if line ends are consistent in source file | ||
513 | local sum = 0 | ||
514 | for _,v in pairs(lineends) do if v > 0 then sum=sum+1 end end | ||
515 | if sum == 1 then | ||
516 | local newline | ||
517 | for k,v in pairs(lineends) do if v ~= 0 then newline = k end end | ||
518 | tgt:write(endlstrip(line2write) .. newline) | ||
519 | else -- newlines are mixed or unknown | ||
520 | tgt:write(line2write) | ||
521 | end | ||
522 | end | ||
523 | end | ||
524 | end | ||
525 | for line in src_readline do | ||
526 | tgt:write(line) | ||
527 | end | ||
528 | tgt:close() | ||
529 | src:close() | ||
530 | return true | ||
531 | end | ||
532 | |||
533 | local function strip_dirs(filename, strip) | ||
534 | if strip == nil then return filename end | ||
535 | for _=1,strip do | ||
536 | filename=filename:gsub("^[^/]*/", "") | ||
537 | end | ||
538 | return filename | ||
539 | end | ||
540 | |||
541 | local function write_new_file(filename, hunk) | ||
542 | local fh = io.open(filename, "wb") | ||
543 | if not fh then return false end | ||
544 | for _, hline in ipairs(hunk.text) do | ||
545 | local c = hline:sub(1,1) | ||
546 | if c ~= "+" and c ~= "-" and c ~= " " then | ||
547 | return false, "malformed patch" | ||
548 | end | ||
549 | fh:write(hline:sub(2)) | ||
550 | end | ||
551 | fh:close() | ||
552 | return true | ||
553 | end | ||
554 | |||
555 | local function patch_file(source, target, epoch, hunks, strip, create_delete) | ||
556 | local create_file = false | ||
557 | if create_delete then | ||
558 | local is_src_epoch = epoch and #hunks == 1 and hunks[1].startsrc == 0 and hunks[1].linessrc == 0 | ||
559 | if is_src_epoch or source == "/dev/null" then | ||
560 | info(format("will create %s", target)) | ||
561 | create_file = true | ||
562 | end | ||
563 | end | ||
564 | if create_file then | ||
565 | return write_new_file(fs.absolute_name(strip_dirs(target, strip)), hunks[1]) | ||
566 | end | ||
567 | source = strip_dirs(source, strip) | ||
568 | local f2patch = source | ||
569 | if not exists(f2patch) then | ||
570 | f2patch = strip_dirs(target, strip) | ||
571 | f2patch = fs.absolute_name(f2patch) | ||
572 | if not exists(f2patch) then --FIX:if f2patch nil | ||
573 | warning(format("source/target file does not exist\n--- %s\n+++ %s", | ||
574 | source, f2patch)) | ||
575 | return false | ||
576 | end | ||
577 | end | ||
578 | if not isfile(f2patch) then | ||
579 | warning(format("not a file - %s", f2patch)) | ||
580 | return false | ||
581 | end | ||
582 | |||
583 | source = f2patch | ||
584 | |||
585 | -- validate before patching | ||
586 | local file = load_file(source) | ||
587 | local hunkno = 1 | ||
588 | local hunk = hunks[hunkno] | ||
589 | local hunkfind = {} | ||
590 | local validhunks = 0 | ||
591 | local canpatch = false | ||
592 | local hunklineno | ||
593 | if not file then | ||
594 | return nil, "failed reading file " .. source | ||
595 | end | ||
596 | |||
597 | if create_delete then | ||
598 | if epoch and #hunks == 1 and hunks[1].starttgt == 0 and hunks[1].linestgt == 0 then | ||
599 | local ok = os.remove(source) | ||
600 | if not ok then | ||
601 | return false | ||
602 | end | ||
603 | info(format("successfully removed %s", source)) | ||
604 | return true | ||
605 | end | ||
606 | end | ||
607 | |||
608 | find_hunks(file, hunks) | ||
609 | |||
610 | local function process_line(line, lineno) | ||
611 | if not hunk or lineno < hunk.startsrc then | ||
612 | return false | ||
613 | end | ||
614 | if lineno == hunk.startsrc then | ||
615 | hunkfind = {} | ||
616 | for _,x in ipairs(hunk.text) do | ||
617 | if x:sub(1,1) == ' ' or x:sub(1,1) == '-' then | ||
618 | hunkfind[#hunkfind+1] = endlstrip(x:sub(2)) | ||
619 | end | ||
620 | end | ||
621 | hunklineno = 1 | ||
622 | |||
623 | -- todo \ No newline at end of file | ||
624 | end | ||
625 | -- check hunks in source file | ||
626 | if lineno < hunk.startsrc + #hunkfind - 1 then | ||
627 | if endlstrip(line) == hunkfind[hunklineno] then | ||
628 | hunklineno = hunklineno + 1 | ||
629 | else | ||
630 | debug(format("hunk no.%d doesn't match source file %s", | ||
631 | hunkno, source)) | ||
632 | -- file may be already patched, but check other hunks anyway | ||
633 | hunkno = hunkno + 1 | ||
634 | if hunkno <= #hunks then | ||
635 | hunk = hunks[hunkno] | ||
636 | return false | ||
637 | else | ||
638 | return true | ||
639 | end | ||
640 | end | ||
641 | end | ||
642 | -- check if processed line is the last line | ||
643 | if lineno == hunk.startsrc + #hunkfind - 1 then | ||
644 | debug(format("file %s hunk no.%d -- is ready to be patched", | ||
645 | source, hunkno)) | ||
646 | hunkno = hunkno + 1 | ||
647 | validhunks = validhunks + 1 | ||
648 | if hunkno <= #hunks then | ||
649 | hunk = hunks[hunkno] | ||
650 | else | ||
651 | if validhunks == #hunks then | ||
652 | -- patch file | ||
653 | canpatch = true | ||
654 | return true | ||
655 | end | ||
656 | end | ||
657 | end | ||
658 | return false | ||
659 | end | ||
660 | |||
661 | local done = false | ||
662 | for lineno, line in ipairs(file) do | ||
663 | done = process_line(line, lineno) | ||
664 | if done then | ||
665 | break | ||
666 | end | ||
667 | end | ||
668 | if not done then | ||
669 | if hunkno <= #hunks and not create_file then | ||
670 | warning(format("premature end of source file %s at hunk %d", | ||
671 | source, hunkno)) | ||
672 | return false | ||
673 | end | ||
674 | end | ||
675 | if validhunks < #hunks then | ||
676 | if check_patched(file, hunks) then | ||
677 | warning(format("already patched %s", source)) | ||
678 | elseif not create_file then | ||
679 | warning(format("source file is different - %s", source)) | ||
680 | return false | ||
681 | end | ||
682 | end | ||
683 | if not canpatch then | ||
684 | return true | ||
685 | end | ||
686 | local backupname = source .. ".orig" | ||
687 | if exists(backupname) then | ||
688 | warning(format("can't backup original file to %s - aborting", | ||
689 | backupname)) | ||
690 | return false | ||
691 | end | ||
692 | local ok = os.rename(source, backupname) | ||
693 | if not ok then | ||
694 | warning(format("failed backing up %s when patching", source)) | ||
695 | return false | ||
696 | end | ||
697 | patch_hunks(backupname, source, hunks) | ||
698 | info(format("successfully patched %s", source)) | ||
699 | os.remove(backupname) | ||
700 | return true | ||
701 | end | ||
702 | |||
703 | function patch.apply_patch(the_patch, strip, create_delete) | ||
704 | local all_ok = true | ||
705 | local total = #the_patch.source | ||
706 | for fileno, source in ipairs(the_patch.source) do | ||
707 | local target = the_patch.target[fileno] | ||
708 | local hunks = the_patch.hunks[fileno] | ||
709 | local epoch = the_patch.epoch[fileno] | ||
710 | info(format("processing %d/%d:\t %s", fileno, total, source)) | ||
711 | local ok = patch_file(source, target, epoch, hunks, strip, create_delete) | ||
712 | all_ok = all_ok and ok | ||
713 | end | ||
714 | -- todo: check for premature eof | ||
715 | return all_ok | ||
716 | end | ||
717 | |||
718 | return patch | ||
diff --git a/tlconfig.lua b/tlconfig.lua index a6667cb1..aefcbef6 100644 --- a/tlconfig.lua +++ b/tlconfig.lua | |||
@@ -1,5 +1,5 @@ | |||
1 | return { | 1 | return { |
2 | build_dir = "build", | 2 | build_dir = "build", |
3 | source_dir = "src", | 3 | source_dir = "src", |
4 | include_dir = { "src" }, | 4 | include_dir = { "src", "types" }, |
5 | } \ No newline at end of file | 5 | } \ No newline at end of file |
diff --git a/src/ltn12.d.tl b/types/ltn12.d.tl index cd2375fc..cd2375fc 100644 --- a/src/ltn12.d.tl +++ b/types/ltn12.d.tl | |||
diff --git a/src/mimetypes.d.tl b/types/mimetypes.d.tl index a735225d..a735225d 100644 --- a/src/mimetypes.d.tl +++ b/types/mimetypes.d.tl | |||
diff --git a/src/socket.d.tl b/types/socket.d.tl index e62e907b..e62e907b 100644 --- a/src/socket.d.tl +++ b/types/socket.d.tl | |||
diff --git a/src/socket/http.d.tl b/types/socket/http.d.tl index 2a64872e..2a64872e 100644 --- a/src/socket/http.d.tl +++ b/types/socket/http.d.tl | |||
diff --git a/src/ssl.d.tl b/types/ssl.d.tl index 1bd40348..1bd40348 100644 --- a/src/ssl.d.tl +++ b/types/ssl.d.tl | |||
diff --git a/src/ssl/https.d.tl b/types/ssl/https.d.tl index 94c38df6..94c38df6 100644 --- a/src/ssl/https.d.tl +++ b/types/ssl/https.d.tl | |||