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

Archive Download this file

Branches

Tags

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