monotone

monotone Mtn Source Tree

Root/std_hooks.lua

1
2-- this is the standard set of lua hooks for monotone;
3-- user-provided files can override it or add to it.
4
5function temp_file()
6 local tdir
7 tdir = os.getenv("TMPDIR")
8 if tdir == nil then tdir = os.getenv("TMP") end
9 if tdir == nil then tdir = os.getenv("TEMP") end
10 if tdir == nil then tdir = "/tmp" end
11 return mkstemp(string.format("%s/mt.XXXXXX", tdir))
12end
13
14function execute(path, ...)
15 local pid
16 local ret = -1
17 pid = spawn(path, unpack(arg))
18 if (pid ~= -1) then ret, pid = wait(pid) end
19 return ret
20end
21
22-- Wrapper around execute to let user confirm in the case where a subprocess
23-- returns immediately
24-- This is needed to work around some brokenness with some merge tools
25-- (e.g. on OS X)
26function execute_confirm(path, ...)
27 execute(path, unpack(arg))
28 print(gettext("Press enter when the subprocess has completed"))
29 io.read()
30 return ret
31end
32
33-- attributes are persistent metadata about files (such as execute
34-- bit, ACLs, various special flags) which we want to have set and
35-- re-set any time the files are modified. the attributes themselves
36-- are stored in a file .mt-attrs, in the working copy (and
37-- manifest). each (f,k,v) triple in an attribute file turns into a
38-- call to attr_functions[k](f,v) in lua.
39
40if (attr_init_functions == nil) then
41 attr_init_functions = {}
42end
43
44attr_init_functions["execute"] =
45 function(filename)
46 if (is_executable(filename)) then
47 return "true"
48 else
49 return nil
50 end
51 end
52
53attr_init_functions["manual_merge"] =
54 function(filename)
55 if (binary_file(filename)) then
56 return "true" -- binary files must merged manually
57 else
58 return nil
59 end
60 end
61
62if (attr_functions == nil) then
63 attr_functions = {}
64end
65
66attr_functions["execute"] =
67 function(filename, value)
68 if (value == "true") then
69 make_executable(filename)
70 end
71 end
72
73
74function ignore_file(name)
75 -- project specific
76 if (ignored_files == nil) then
77 ignored_files = {}
78 local ignfile = io.open(".mt-ignore", "r")
79 if (ignfile ~= nil) then
80 local line = ignfile:read()
81 while (line ~= nil)
82 do
83 table.insert(ignored_files, line)
84 line = ignfile:read()
85 end
86 io.close(ignfile)
87 end
88 end
89 for i, line in pairs(ignored_files)
90 do
91 local pcallstatus, result = pcall(function() if (regex.search(line, name)) then return true else return false end end)
92 if pcallstatus == true then
93 -- no error from the regex.search call
94 if result == true then return true end
95 else
96 -- regex.search had a problem, warn the user their .mt-ignore file syntax is wrong
97 io.stderr:write("WARNING: the line '" .. line .. "' in your .mt-ignore file caused error '" .. result .. "'"
98 .. " while matching filename '" .. name .. "'.\nignoring this regex for all remaining files.\n")
99 table.remove(ignored_files, i)
100 end
101 end
102 -- c/c++
103 if (string.find(name, "%.a$")) then return true end
104 if (string.find(name, "%.so$")) then return true end
105 if (string.find(name, "%.o$")) then return true end
106 if (string.find(name, "%.la$")) then return true end
107 if (string.find(name, "%.lo$")) then return true end
108 if (string.find(name, "^core$")) then return true end
109 if (string.find(name, "/core$")) then return true end
110 if (string.find(name, "/core%.%d+$")) then return true end
111 -- python
112 if (string.find(name, "%.pyc$")) then return true end
113 if (string.find(name, "%.pyo$")) then return true end
114 -- TeX
115 if (string.find(name, "%.aux$")) then return true end
116 -- backup files
117 if (string.find(name, "%.bak$")) then return true end
118 if (string.find(name, "%.orig$")) then return true end
119 if (string.find(name, "%.rej$")) then return true end
120 if (string.find(name, "%~$")) then return true end
121 -- editor temp files
122 -- vim creates .foo.swp files
123 if (string.find(name, "%.[^/]*%.swp$")) then return true end
124 -- emacs creates #foo# files
125 if (string.find(name, "%#[^/]*%#$")) then return true end
126 -- autotools detritus:
127 if (string.find(name, "^autom4te.cache/")) then return true end
128 if (string.find(name, "/autom4te.cache/")) then return true end
129 if (string.find(name, "^.deps/")) then return true end
130 if (string.find(name, "/.deps/")) then return true end
131 -- Cons/SCons detritus:
132 if (string.find(name, "^.consign$")) then return true end
133 if (string.find(name, "/.consign$")) then return true end
134 if (string.find(name, "^.sconsign$")) then return true end
135 if (string.find(name, "/.sconsign$")) then return true end
136 -- other VCSes:
137 if (string.find(name, "^CVS/")) then return true end
138 if (string.find(name, "/CVS/")) then return true end
139 if (string.find(name, "^%.svn/")) then return true end
140 if (string.find(name, "/%.svn/")) then return true end
141 if (string.find(name, "^SCCS/")) then return true end
142 if (string.find(name, "/SCCS/")) then return true end
143 if (string.find(name, "^_darcs/")) then return true end
144 if (string.find(name, "^.cdv/")) then return true end
145 if (string.find(name, "^.git/")) then return true end
146 if (string.find(name, "%.scc$")) then return true end
147 -- desktop/directory configuration metadata
148 if (string.find(name, "^.DS_Store$")) then return true end
149 if (string.find(name, "/.DS_Store$")) then return true end
150 if (string.find(name, "^desktop.ini$")) then return true end
151 if (string.find(name, "/desktop.ini$")) then return true end
152 return false;
153end
154
155-- return true means "binary", false means "text",
156-- nil means "unknown, try to guess"
157function binary_file(name)
158 local lowname=string.lower(name)
159 -- some known binaries, return true
160 if (string.find(lowname, "%.gif$")) then return true end
161 if (string.find(lowname, "%.jpe?g$")) then return true end
162 if (string.find(lowname, "%.png$")) then return true end
163 if (string.find(lowname, "%.bz2$")) then return true end
164 if (string.find(lowname, "%.gz$")) then return true end
165 if (string.find(lowname, "%.zip$")) then return true end
166 if (string.find(lowname, "%.class$")) then return true end
167 if (string.find(lowname, "%.jar$")) then return true end
168 if (string.find(lowname, "%.war$")) then return true end
169 if (string.find(lowname, "%.ear$")) then return true end
170 -- some known text, return false
171 if (string.find(lowname, "%.cc?$")) then return false end
172 if (string.find(lowname, "%.cxx$")) then return false end
173 if (string.find(lowname, "%.hh?$")) then return false end
174 if (string.find(lowname, "%.hxx$")) then return false end
175 if (string.find(lowname, "%.lua$")) then return false end
176 if (string.find(lowname, "%.texi$")) then return false end
177 if (string.find(lowname, "%.sql$")) then return false end
178 if (string.find(lowname, "%.java$")) then return false end
179 -- unknown - read file and use the guess-binary
180 -- monotone built-in function
181 return guess_binary_file_contents(name)
182end
183
184function edit_comment(basetext, user_log_message)
185 local exe = nil
186 if (program_exists_in_path("vi")) then exe = "vi" end
187 if (program_exists_in_path("notepad.exe")) then exe = "notepad.exe" end
188 local visual = os.getenv("VISUAL")
189 if (visual ~= nil) then exe = visual end
190 local editor = os.getenv("EDITOR")
191 if (editor ~= nil) then exe = editor end
192
193 if (exe == nil) then
194 io.write("Could not find editor to enter commit message\n"
195 .. "Try setting the environment variable EDITOR\n")
196 return nil
197 end
198
199 local tmp, tname = temp_file()
200 if (tmp == nil) then return nil end
201 basetext = "MT: " .. string.gsub(basetext, "\n", "\nMT: ") .. "\n"
202 if user_log_message == "" then
203 tmp:write("\n")
204 else
205 tmp:write(user_log_message)
206 end
207 tmp:write(basetext)
208 io.close(tmp)
209
210 if (execute(exe, tname) ~= 0) then
211 io.write(string.format(gettext("Error running editor '%s' to enter log message\n"),
212 exe))
213 os.remove(tname)
214 return nil
215 end
216
217 tmp = io.open(tname, "r")
218 if (tmp == nil) then os.remove(tname); return nil end
219 local res = ""
220 local line = tmp:read()
221 while(line ~= nil) do
222 if (not string.find(line, "^MT:")) then
223 res = res .. line .. "\n"
224 end
225 line = tmp:read()
226 end
227 io.close(tmp)
228 os.remove(tname)
229 return res
230end
231
232
233function non_blocking_rng_ok()
234 return true
235end
236
237
238function persist_phrase_ok()
239 return true
240end
241
242
243function use_inodeprints()
244 return false
245end
246
247
248-- trust evaluation hooks
249
250function intersection(a,b)
251 local s={}
252 local t={}
253 for k,v in pairs(a) do s[v] = 1 end
254 for k,v in pairs(b) do if s[v] ~= nil then table.insert(t,v) end end
255 return t
256end
257
258function get_revision_cert_trust(signers, id, name, val)
259 return true
260end
261
262function get_manifest_cert_trust(signers, id, name, val)
263 return true
264end
265
266function get_file_cert_trust(signers, id, name, val)
267 return true
268end
269
270function accept_testresult_change(old_results, new_results)
271 local reqfile = io.open("MT/wanted-testresults", "r")
272 if (reqfile == nil) then return true end
273 local line = reqfile:read()
274 local required = {}
275 while (line ~= nil)
276 do
277 required[line] = true
278 line = reqfile:read()
279 end
280 io.close(reqfile)
281 for test, res in pairs(required)
282 do
283 if old_results[test] == true and new_results[test] ~= true
284 then
285 return false
286 end
287 end
288 return true
289end
290
291-- merger support
292
293function merge2_meld_cmd(lfile, rfile)
294 return
295 function()
296 return execute("meld", lfile, rfile)
297 end
298end
299
300function merge3_meld_cmd(lfile, afile, rfile)
301 return
302 function()
303 return execute("meld", lfile, afile, rfile)
304 end
305end
306
307function merge2_tortoise_cmd(lfile, rfile, outfile)
308 return
309 function()
310 return execute("tortoisemerge",
311 string.format("/theirs:%s", lfile),
312 string.format("/mine:%s", rfile),
313 string.format("/merged:%s", outfile))
314 end
315end
316
317function merge3_tortoise_cmd(lfile, afile, rfile, outfile)
318 return
319 function()
320 return execute("tortoisemerge",
321 string.format("/base:%s", afile),
322 string.format("/theirs:%s", lfile),
323 string.format("/mine:%s", rfile),
324 string.format("/merged:%s", outfile))
325 end
326end
327
328function merge2_vim_cmd(vim, lfile, rfile, outfile)
329 return
330 function()
331 return execute(vim, "-f", "-d", "-c", string.format("file %s", outfile),
332 lfile, rfile)
333 end
334end
335
336function merge3_vim_cmd(vim, afile, lfile, rfile, outfile)
337 return
338 function()
339 return execute(vim, "-f", "-d", "-c", string.format("file %s", outfile),
340 afile, lfile, rfile)
341 end
342end
343
344function merge3_rcsmerge_vim_cmd(merge, vim, lfile, afile, rfile, outfile)
345 return
346 function()
347 -- XXX: This is tough - should we check if conflict markers stay or not?
348 -- If so, we should certainly give the user some way to still force
349 -- the merge to proceed since they can appear in the files (and I saw
350 -- that). --pasky
351 if execute(merge, lfile, afile, rfile) == 0 then
352 copy_text_file(lfile, outfile);
353 return 0
354 end
355 return execute(vim, "-f", "-c", string.format("file %s", outfile),
356 lfile)
357 end
358end
359
360function merge2_emacs_cmd(emacs, lfile, rfile, outfile)
361 local elisp = "(ediff-merge-files \"%s\" \"%s\" nil \"%s\")"
362 return
363 function()
364 return execute(emacs, "--eval",
365 string.format(elisp, lfile, rfile, outfile))
366 end
367end
368
369function merge3_emacs_cmd(emacs, lfile, afile, rfile, outfile)
370 local elisp = "(ediff-merge-files-with-ancestor \"%s\" \"%s\" \"%s\" nil \"%s\")"
371 return
372 function()
373 return execute(emacs, "--eval",
374 string.format(elisp, lfile, rfile, afile, outfile))
375 end
376end
377
378function merge2_xxdiff_cmd(left_path, right_path, merged_path, lfile, rfile, outfile)
379 return
380 function()
381 return execute("xxdiff",
382 "--title1", left_path,
383 "--title2", right_path,
384 lfile, rfile,
385 "--merged-filename", outfile)
386 end
387end
388
389function merge3_xxdiff_cmd(left_path, anc_path, right_path, merged_path,
390 lfile, afile, rfile, outfile)
391 return
392 function()
393 return execute("xxdiff",
394 "--title1", left_path,
395 "--title2", right_path,
396 "--title3", merged_path,
397 lfile, afile, rfile,
398 "--merge",
399 "--merged-filename", outfile)
400 end
401end
402
403function merge2_kdiff3_cmd(left_path, right_path, merged_path, lfile, rfile, outfile)
404 return
405 function()
406 return execute("kdiff3",
407 "--L1", left_path,
408 "--L2", right_path,
409 lfile, rfile,
410 "-o", outfile)
411 end
412end
413
414function merge3_kdiff3_cmd(left_path, anc_path, right_path, merged_path,
415 lfile, afile, rfile, outfile)
416 return
417 function()
418 return execute("kdiff3",
419 "--L1", anc_path,
420 "--L2", left_path,
421 "--L3", right_path,
422 afile, lfile, rfile,
423 "--merge",
424 "--o", outfile)
425 end
426end
427
428function merge2_opendiff_cmd(left_path, right_path, merged_path, lfile, rfile, outfile)
429 return
430 function()
431 -- As opendiff immediately returns, let user confirm manually
432 return execute_confirm("opendiff",lfile,rfile,"-merge",outfile)
433 end
434end
435
436function merge3_opendiff_cmd(left_path, anc_path, right_path, merged_path, lfile, afile, rfile, outfile)
437 return
438 function()
439 -- As opendiff immediately returns, let user confirm manually
440 execute_confirm("opendiff",lfile,rfile,"-ancestor",afile,"-merge",outfile)
441 end
442end
443
444function write_to_temporary_file(data)
445 tmp, filename = temp_file()
446 if (tmp == nil) then
447 return nil
448 end;
449 tmp:write(data)
450 io.close(tmp)
451 return filename
452end
453
454function copy_text_file(srcname, destname)
455 src = io.open(srcname, "r")
456 if (src == nil) then return nil end
457 dest = io.open(destname, "w")
458 if (dest == nil) then return nil end
459
460 while true do
461 local line = src:read()
462 if line == nil then break end
463 dest:write(line, "\n")
464 end
465
466 io.close(dest)
467 io.close(src)
468end
469
470function read_contents_of_file(filename, mode)
471 tmp = io.open(filename, mode)
472 if (tmp == nil) then
473 return nil
474 end
475 local data = tmp:read("*a")
476 io.close(tmp)
477 return data
478end
479
480function program_exists_in_path(program)
481 return existsonpath(program) == 0
482end
483
484function get_preferred_merge2_command (tbl)
485 local cmd = nil
486 local left_path = tbl.left_path
487 local right_path = tbl.right_path
488 local merged_path = tbl.merged_path
489 local lfile = tbl.lfile
490 local rfile = tbl.rfile
491 local outfile = tbl.outfile
492
493 local editor = os.getenv("EDITOR")
494 if editor ~= nil then editor = string.lower(editor) else editor = "" end
495
496
497 if program_exists_in_path("kdiff3") then
498 cmd = merge2_kdiff3_cmd (left_path, right_path, merged_path, lfile, rfile, outfile)
499 elseif program_exists_in_path ("xxdiff") then
500 cmd = merge2_xxdiff_cmd (left_path, right_path, merged_path, lfile, rfile, outfile)
501 elseif program_exists_in_path ("opendiff") then
502 cmd = merge2_opendiff_cmd (left_path, right_path, merged_path, lfile, rfile, outfile)
503 elseif program_exists_in_path ("TortoiseMerge") then
504 cmd = merge2_tortoise_cmd(lfile, rfile, outfile)
505 elseif string.find(editor, "emacs") ~= nil or string.find(editor, "gnu") ~= nil then
506 if string.find(editor, "xemacs") and program_exists_in_path("xemacs") then
507 cmd = merge2_emacs_cmd ("xemacs", lfile, rfile, outfile)
508 elseif program_exists_in_path("emacs") then
509 cmd = merge2_emacs_cmd ("emacs", lfile, rfile, outfile)
510 end
511 elseif string.find(editor, "vim") ~= nil then
512 io.write (string.format("\nWARNING: 'vim' was choosen to perform external 2-way merge.\n"..
513 "You should merge all changes to *LEFT* file due to limitation of program\n"..
514 "arguments.\n\n"))
515 if os.getenv ("DISPLAY") ~= nil and program_exists_in_path ("gvim") then
516 cmd = merge2_vim_cmd ("gvim", lfile, rfile, outfile)
517 elseif program_exists_in_path ("vim") then
518 cmd = merge2_vim_cmd ("vim", lfile, rfile, outfile)
519 end
520 elseif program_exists_in_path ("meld") then
521 tbl.meld_exists = true
522 io.write (string.format("\nWARNING: 'meld' was choosen to perform external 2-way merge.\n"..
523 "You should merge all changes to *LEFT* file due to limitation of program\n"..
524 "arguments.\n\n"))
525 cmd = merge2_meld_cmd (lfile, rfile)
526 end
527 return cmd
528end
529
530function merge2 (left_path, right_path, merged_path, left, right)
531 local ret = nil
532 local tbl = {}
533
534 tbl.lfile = nil
535 tbl.rfile = nil
536 tbl.outfile = nil
537 tbl.meld_exists = false
538
539 tbl.lfile = write_to_temporary_file (left)
540 tbl.rfile = write_to_temporary_file (right)
541 tbl.outfile = write_to_temporary_file ("")
542
543 if tbl.lfile ~= nil and tbl.rfile ~= nil and tbl.outfile ~= nil
544 then
545 tbl.left_path = left_path
546 tbl.right_path = right_path
547 tbl.merged_path = merged_path
548
549 local cmd = get_preferred_merge2_command (tbl)
550
551 if cmd ~=nil
552 then
553 io.write (string.format(gettext("executing external 2-way merge command\n")))
554 cmd ()
555 if tbl.meld_exists
556 then
557 ret = read_contents_of_file (tbl.lfile, "r")
558 else
559 ret = read_contents_of_file (tbl.outfile, "r")
560 end
561 if string.len (ret) == 0
562 then
563 ret = nil
564 end
565 else
566 io.write (string.format("No external 2-way merge command found.\n"..
567 "You may want to check that $EDITOR is set to an editor that supports 2-way merge,\n"..
568 "set this explicitly in your get_preferred_merge2_command hook,\n"..
569 "or add a 2-way merge program to your path.\n\n"))
570 end
571 end
572
573 os.remove (tbl.lfile)
574 os.remove (tbl.rfile)
575 os.remove (tbl.outfile)
576
577 return ret
578end
579
580function get_preferred_merge3_command (tbl)
581 local cmd = nil
582 local left_path = tbl.left_path
583 local anc_path = tbl.anc_path
584 local right_path = tbl.right_path
585 local merged_path = tbl.merged_path
586 local lfile = tbl.lfile
587 local afile = tbl.afile
588 local rfile = tbl.rfile
589 local outfile = tbl.outfile
590
591 local editor = os.getenv("EDITOR")
592 if editor ~= nil then editor = string.lower(editor) else editor = "" end
593
594 local merge = os.getenv("MTMERGE")
595 -- TODO: Support for rcsmerge_emacs
596 if merge ~= nil and string.find(editor, "vim") ~= nil then
597 if os.getenv ("DISPLAY") ~= nil and program_exists_in_path ("gvim") then
598 cmd = merge3_rcsmerge_vim_cmd (merge, "gvim", lfile, afile, rfile, outfile)
599 elseif program_exists_in_path ("vim") then
600 cmd = merge3_rcsmerge_vim_cmd (merge, "vim", lfile, afile, rfile, outfile)
601 end
602
603 elseif program_exists_in_path("kdiff3") then
604 cmd = merge3_kdiff3_cmd (left_path, anc_path, right_path, merged_path, lfile, afile, rfile, outfile)
605 elseif program_exists_in_path ("xxdiff") then
606 cmd = merge3_xxdiff_cmd (left_path, anc_path, right_path, merged_path, lfile, afile, rfile, outfile)
607 elseif program_exists_in_path ("opendiff") then
608 cmd = merge3_opendiff_cmd (left_path, anc_path, right_path, merged_path, lfile, afile, rfile, outfile)
609 elseif program_exists_in_path ("TortoiseMerge") then
610 cmd = merge3_tortoise_cmd(lfile, afile, rfile, outfile)
611 elseif string.find(editor, "emacs") ~= nil or string.find(editor, "gnu") ~= nil then
612 if string.find(editor, "xemacs") and program_exists_in_path ("xemacs") then
613 cmd = merge3_emacs_cmd ("xemacs", lfile, afile, rfile, outfile)
614 elseif program_exists_in_path ("emacs") then
615 cmd = merge3_emacs_cmd ("emacs", lfile, afile, rfile, outfile)
616 end
617 elseif string.find(editor, "vim") ~= nil then
618 io.write (string.format("\nWARNING: 'vim' was choosen to perform external 3-way merge.\n"..
619 "You should merge all changes to *LEFT* file due to limitation of program\n"..
620 "arguments. The order of the files is ancestor, left, right.\n\n"))
621 if os.getenv ("DISPLAY") ~= nil and program_exists_in_path ("gvim") then
622 cmd = merge3_vim_cmd ("gvim", afile, lfile, rfile, outfile)
623 elseif program_exists_in_path ("vim") then
624 cmd = merge3_vim_cmd ("vim", afile, lfile, rfile, outfile)
625 end
626 elseif program_exists_in_path ("meld") then
627 tbl.meld_exists = true
628 io.write (string.format("\nWARNING: 'meld' was choosen to perform external 3-way merge.\n"..
629 "You should merge all changes to *CENTER* file due to limitation of program\n"..
630 "arguments.\n\n"))
631 cmd = merge3_meld_cmd (lfile, afile, rfile)
632 end
633
634 return cmd
635end
636
637function merge3 (anc_path, left_path, right_path, merged_path, ancestor, left, right)
638 local ret
639 local tbl = {}
640
641 tbl.anc_path = anc_path
642 tbl.left_path = left_path
643 tbl.right_path = right_path
644
645 tbl.merged_path = merged_path
646 tbl.afile = nil
647 tbl.lfile = nil
648 tbl.rfile = nil
649 tbl.outfile = nil
650 tbl.meld_exists = false
651 tbl.lfile = write_to_temporary_file (left)
652 tbl.afile = write_to_temporary_file (ancestor)
653 tbl.rfile = write_to_temporary_file (right)
654 tbl.outfile = write_to_temporary_file ("")
655
656 if tbl.lfile ~= nil and tbl.rfile ~= nil and tbl.afile ~= nil and tbl.outfile ~= nil
657 then
658 local cmd = get_preferred_merge3_command (tbl)
659 if cmd ~=nil
660 then
661 io.write (string.format(gettext("executing external 3-way merge command\n")))
662 cmd ()
663 if tbl.meld_exists
664 then
665 ret = read_contents_of_file (tbl.afile, "r")
666 else
667 ret = read_contents_of_file (tbl.outfile, "r")
668 end
669 if string.len (ret) == 0
670 then
671 ret = nil
672 end
673 else
674 io.write (string.format("No external 3-way merge command found.\n"..
675 "You may want to check that $EDITOR is set to an editor that supports 3-way merge,\n"..
676 "set this explicitly in your get_preferred_merge3_command hook,\n"..
677 "or add a 3-way merge program to your path.\n\n"))
678 end
679 end
680
681 os.remove (tbl.lfile)
682 os.remove (tbl.rfile)
683 os.remove (tbl.afile)
684 os.remove (tbl.outfile)
685
686 return ret
687end
688
689-- expansion of values used in selector completion
690
691function expand_selector(str)
692
693 -- something which looks like a generic cert pattern
694 if string.find(str, "^[^=]*=.*$")
695 then
696 return ("c:" .. str)
697 end
698
699 -- something which looks like an email address
700 if string.find(str, "[%w%-_]+@[%w%-_]+")
701 then
702 return ("a:" .. str)
703 end
704
705 -- something which looks like a branch name
706 if string.find(str, "[%w%-]+%.[%w%-]+")
707 then
708 return ("b:" .. str)
709 end
710
711 -- a sequence of nothing but hex digits
712 if string.find(str, "^%x+$")
713 then
714 return ("i:" .. str)
715 end
716
717 -- tries to expand as a date
718 local dtstr = expand_date(str)
719 if dtstr ~= nil
720 then
721 return ("d:" .. dtstr)
722 end
723
724 return nil
725end
726
727-- expansion of a date expression
728function expand_date(str)
729 -- simple date patterns
730 if string.find(str, "^19%d%d%-%d%d")
731 or string.find(str, "^20%d%d%-%d%d")
732 then
733 return (str)
734 end
735
736 -- "now"
737 if str == "now"
738 then
739 local t = os.time(os.date('!*t'))
740 return os.date("%FT%T", t)
741 end
742
743 -- today don't uses the time
744 if str == "today"
745 then
746 local t = os.time(os.date('!*t'))
747 return os.date("%F", t)
748 end
749
750 -- "yesterday", the source of all hangovers
751 if str == "yesterday"
752 then
753 local t = os.time(os.date('!*t'))
754 return os.date("%F", t - 86400)
755 end
756
757 -- "CVS style" relative dates such as "3 weeks ago"
758 local trans = {
759 minute = 60;
760 hour = 3600;
761 day = 86400;
762 week = 604800;
763 month = 2678400;
764 year = 31536000
765 }
766 local pos, len, n, type = string.find(str, "(%d+) ([minutehordaywk]+)s? ago")
767 if trans[type] ~= nil
768 then
769 local t = os.time(os.date('!*t'))
770 if trans[type] <= 3600
771 then
772 return os.date("%FT%T", t - (n * trans[type]))
773 else
774 return os.date("%F", t - (n * trans[type]))
775 end
776 end
777
778 return nil
779end
780
781
782external_diff_default_args = "-u"
783
784-- default external diff, works for gnu diff
785function external_diff(file_path, data_old, data_new, is_binary, diff_args, rev_old, rev_new)
786 local old_file = write_to_temporary_file(data_old);
787 local new_file = write_to_temporary_file(data_new);
788
789 if diff_args == nil then diff_args = external_diff_default_args end
790 execute("diff", diff_args, "--label", file_path .. "\told", old_file, "--label", file_path .. "\tnew", new_file);
791
792 os.remove (old_file);
793 os.remove (new_file);
794end
795
796-- netsync permissions hooks (and helper)
797
798function globish_match(glob, str)
799 local pcallstatus, result = pcall(function() if (globish.match(glob, str)) then return true else return false end end)
800 if pcallstatus == true then
801 -- no error
802 return result
803 else
804 -- globish.match had a problem
805 return nil
806 end
807end
808
809function get_netsync_read_permitted(branch, ident)
810 local permfile = io.open(get_confdir() .. "/read-permissions", "r")
811 if (permfile == nil) then return false end
812 local dat = permfile:read("*a")
813 local res = parse_basic_io(dat)
814 if res == nil then
815 io.stderr:write("file read-permissions cannot be parsed\n")
816 return false
817 end
818 local matches = false
819 local cont = false
820 for i, item in pairs(res)
821 do
822 -- legal names: pattern, allow, deny, continue
823 if item.name == "pattern" then
824 if matches and not cont then return false end
825 matches = false
826 for j, val in pairs(item.values) do
827 if globish_match(val, branch) then matches = true end
828 end
829 elseif item.name == "allow" then if matches then
830 for j, val in pairs(item.values) do
831 if val == "*" then return true end
832 if globish_match(val, ident) then return true end
833 end
834 end elseif item.name == "deny" then if matches then
835 for j, val in pairs(item.values) do
836 if globish_match(val, ident) then return false end
837 end
838 end elseif item.name == "continue" then if matches then
839 cont = true
840 for j, val in pairs(item.values) do
841 if val == "false" or val == "no" then cont = false end
842 end
843 end elseif item.name ~= "comment" then
844 io.stderr:write("unknown symbol in read-permissions: " .. item.name .. "\n")
845 return false
846 end
847 end
848 return false
849end
850
851function get_netsync_write_permitted(ident)
852 local permfile = io.open(get_confdir() .. "/write-permissions", "r")
853 if (permfile == nil) then
854 return false
855 end
856 local line = permfile:read()
857 while (line ~= nil) do
858 local _, _, ln = string.find(line, "%s*([^%s]*)%s*")
859 if ln == "*" then return true end
860 if globish_match(ln, ident) then return true end
861 line = permfile:read()
862 end
863 io.close(permfile)
864 return false
865end

Archive Download this file

Branches

Tags

Quick Links:     www.monotone.ca    -     Downloads    -     Documentation    -     Wiki    -     Code Forge    -     Build Status