monotone

monotone Mtn Source Tree

Root/lua.cc

1// copyright (C) 2002, 2003 graydon hoare <graydon@pobox.com>
2// all rights reserved.
3// licensed to the public under the terms of the GNU GPL (>= 2)
4// see the file COPYING for details
5
6#include "config.h"
7
8extern "C" {
9#include <lua.h>
10#include <lualib.h>
11}
12
13#include <errno.h>
14#include <stdlib.h>
15#include <string.h>
16#include <stdarg.h>
17#include <signal.h>
18#include <boost/lexical_cast.hpp>
19#include <boost/filesystem/path.hpp>
20#include <boost/filesystem/operations.hpp>
21
22#include <set>
23#include <map>
24
25#include "app_state.hh"
26#include "file_io.hh"
27#include "lua.hh"
28#include "mkstemp.hh"
29#include "sanity.hh"
30#include "vocab.hh"
31#include "platform.hh"
32
33// defined in {std,test}_hooks.lua, converted
34#include "test_hooks.h"
35#include "std_hooks.h"
36
37using namespace std;
38using boost::lexical_cast;
39
40/*
41static int panic_thrower(lua_State * st)
42{
43 throw oops("lua panic");
44}
45*/
46
47extern "C"
48{
49 static int
50 monotone_mkstemp_for_lua(lua_State *L)
51 {
52 int fd = -1;
53 FILE **pf = NULL;
54 char const *filename = lua_tostring (L, -1);
55 std::string dup(filename);
56
57 fd = monotone_mkstemp(dup);
58
59 if (fd == -1)
60 return 0;
61
62 // this magic constructs a lua object which the lua io library
63 // will enjoy working with
64 pf = static_cast<FILE **>(lua_newuserdata(L, sizeof(FILE *)));
65 *pf = fdopen(fd, "r+");
66 lua_pushstring(L, "FILE*");
67 lua_rawget(L, LUA_REGISTRYINDEX);
68 lua_setmetatable(L, -2);
69
70 lua_pushstring(L, dup.c_str());
71
72 if (*pf == NULL)
73 {
74 lua_pushnil(L);
75 lua_pushfstring(L, "%s", strerror(errno));
76 lua_pushnumber(L, errno);
77 return 3;
78 }
79 else
80 return 2;
81 }
82
83 static int
84 monotone_existsonpath_for_lua(lua_State *L)
85 {
86 const char *exe = lua_tostring(L, -1);
87 lua_pushnumber(L, existsonpath(exe));
88 return 1;
89 }
90
91 static int
92 monotone_make_executable_for_lua(lua_State *L)
93 {
94 const char *path = lua_tostring(L, -1);
95 lua_pushnumber(L, make_executable(path));
96 return 1;
97 }
98
99 static int
100 monotone_spawn_for_lua(lua_State *L)
101 {
102 int n = lua_gettop(L);
103 const char *path = lua_tostring(L, -n);
104 char **argv = (char**)malloc((n+1)*sizeof(char*));
105 int ret, i;
106 if (argv==NULL)
107 return 0;
108 argv[0] = (char*)path;
109 for (i=1; i<n; i++) argv[i] = (char*)lua_tostring(L, -(n - i));
110 argv[i] = NULL;
111 ret = process_spawn(argv);
112 free(argv);
113 lua_pushnumber(L, ret);
114 return 1;
115 }
116
117 static int
118 monotone_wait_for_lua(lua_State *L)
119 {
120 int pid = (int)lua_tonumber(L, -1);
121 int res;
122 int ret;
123 ret = process_wait(pid, &res);
124 lua_pushnumber(L, res);
125 lua_pushnumber(L, ret);
126 return 2;
127 }
128
129 static int
130 monotone_kill_for_lua(lua_State *L)
131 {
132 int n = lua_gettop(L);
133 int pid = (int)lua_tonumber(L, -2);
134 int sig;
135 if (n>1)
136 sig = (int)lua_tonumber(L, -1);
137 else
138 sig = SIGTERM;
139 lua_pushnumber(L, process_kill(pid, sig));
140 return 1;
141 }
142
143 static int
144 monotone_sleep_for_lua(lua_State *L)
145 {
146 int seconds = (int)lua_tonumber(L, -1);
147 lua_pushnumber(L, process_sleep(seconds));
148 return 1;
149 }
150}
151
152
153lua_hooks::lua_hooks()
154{
155 st = lua_open ();
156 I(st);
157
158 // no atpanic support in 4.x
159 // lua_atpanic (st, &panic_thrower);
160
161 luaopen_base(st);
162 luaopen_io(st);
163 luaopen_string(st);
164 luaopen_math(st);
165 luaopen_table(st);
166 luaopen_debug(st);
167
168 // add monotone-specific functions
169 lua_register(st, "mkstemp", monotone_mkstemp_for_lua);
170 lua_register(st, "existsonpath", monotone_existsonpath_for_lua);
171 lua_register(st, "make_executable", monotone_make_executable_for_lua);
172 lua_register(st, "spawn", monotone_spawn_for_lua);
173 lua_register(st, "wait", monotone_wait_for_lua);
174 lua_register(st, "kill", monotone_kill_for_lua);
175 lua_register(st, "sleep", monotone_sleep_for_lua);
176}
177
178lua_hooks::~lua_hooks()
179{
180 if (st)
181 lua_close (st);
182}
183
184
185// This Lua object represents a single imperative transaction with the lua
186// interpreter. if it fails at any point, all further commands in the
187// transaction are ignored. it cleans the lua stack up when it is
188// destructed, so no need to pop values when you're done.
189
190struct
191Lua
192{
193 lua_State * st;
194 bool failed;
195
196 Lua(lua_State * s) :
197 st(s), failed(false)
198 {}
199
200 ~Lua()
201 {
202 lua_settop(st, 0);
203 }
204
205 bool ok()
206 {
207 return !failed;
208 }
209
210 // getters
211
212 Lua & get(int idx = LUA_GLOBALSINDEX)
213 {
214 if (failed) return *this;
215 if (!lua_istable (st, idx))
216 {
217 L(F("lua istable() failed\n"));
218 failed = true;
219 return *this;
220 }
221 if (lua_gettop (st) < 1)
222 {
223 L(F("lua stack top > 0 failed\n"));
224 failed = true;
225 return *this;
226 }
227 lua_gettable(st, idx);
228 return *this;
229 }
230
231 Lua & get_fn(int idx = LUA_GLOBALSINDEX)
232 {
233 if (failed) return *this;
234 get(idx);
235 if (!lua_isfunction (st, -1))
236 {
237 L(F("lua isfunction() failed in get_fn\n"));
238 failed = true;
239 }
240 return *this;
241 }
242
243 Lua & get_tab(int idx = LUA_GLOBALSINDEX)
244 {
245 if (failed) return *this;
246 get(idx);
247 if (!lua_istable (st, -1))
248 {
249 L(F("lua istable() failed in get_tab\n"));
250 failed = true;
251 }
252 return *this;
253 }
254
255 Lua & get_str(int idx = LUA_GLOBALSINDEX)
256 {
257 if (failed) return *this;
258 get(idx);
259 if (!lua_isstring (st, -1))
260 {
261 L(F("lua isstring() failed in get_str\n"));
262 failed = true;
263 }
264 return *this;
265 }
266
267 Lua & get_num(int idx = LUA_GLOBALSINDEX)
268 {
269 if (failed) return *this;
270 get(idx);
271 if (!lua_isnumber (st, -1))
272 {
273 L(F("lua isnumber() failed in get_num\n"));
274 failed = true;
275 }
276 return *this;
277 }
278
279 Lua & get_bool(int idx = LUA_GLOBALSINDEX)
280 {
281 if (failed) return *this;
282 get(idx);
283 if (!lua_isboolean (st, -1))
284 {
285 L(F("lua isboolean() failed in get_bool\n"));
286 failed = true;
287 }
288 return *this;
289 }
290
291 // extractors
292
293 Lua & extract_str(string & str)
294 {
295 if (failed) return *this;
296 if (!lua_isstring (st, -1))
297 {
298 L(F("lua isstring() failed in extract_str\n"));
299 failed = true;
300 return *this;
301 }
302 str = string(lua_tostring(st, -1), lua_strlen(st, -1));
303 return *this;
304 }
305
306 Lua & extract_int(int & i)
307 {
308 if (failed) return *this;
309 if (!lua_isnumber (st, -1))
310 {
311 L(F("lua isnumber() failed in extract_int\n"));
312 failed = true;
313 return *this;
314 }
315 i = static_cast<int>(lua_tonumber(st, -1));
316 return *this;
317 }
318
319 Lua & extract_double(double & i)
320 {
321 if (failed) return *this;
322 if (!lua_isnumber (st, -1))
323 {
324 L(F("lua isnumber() failed in extract_double\n"));
325 failed = true;
326 return *this;
327 }
328 i = lua_tonumber(st, -1);
329 return *this;
330 }
331
332
333 Lua & extract_bool(bool & i)
334 {
335 if (failed) return *this;
336 if (!lua_isboolean (st, -1))
337 {
338 L(F("lua isboolean() failed in extract_bool\n"));
339 failed = true;
340 return *this;
341 }
342 i = (lua_toboolean(st, -1) == 1);
343 return *this;
344 }
345
346
347 // table iteration
348
349 Lua & begin()
350 {
351 if (failed) return *this;
352 if (!lua_istable(st, -1))
353 {
354 L(F("lua istable() failed in begin\n"));
355 failed = true;
356 return *this;
357 }
358 I(lua_checkstack (st, 1));
359 lua_pushnil(st);
360 return *this;
361 }
362
363 bool next()
364 {
365 if (failed) return false;
366 if (!lua_istable(st, -2))
367 {
368 L(F("lua istable() failed in next\n"));
369 failed = true;
370 return false;
371 }
372 I(lua_checkstack (st, 1));
373 if (lua_next(st, -2) != 0)
374 {
375 return true;
376 }
377 pop();
378 return false;
379 }
380
381 // pushers
382
383 Lua & push_str(string const & str)
384 {
385 if (failed) return *this;
386 I(lua_checkstack (st, 1));
387 lua_pushlstring(st, str.c_str(), str.size());
388 return *this;
389 }
390
391 Lua & push_int(int num)
392 {
393 if (failed) return *this;
394 I(lua_checkstack (st, 1));
395 lua_pushnumber(st, num);
396 return *this;
397 }
398
399 Lua & push_int(double num)
400 {
401 if (failed) return *this;
402 I(lua_checkstack (st, 1));
403 lua_pushnumber(st, num);
404 return *this;
405 }
406
407 Lua & push_bool(bool b)
408 {
409 if (failed) return *this;
410 I(lua_checkstack (st, 1));
411 lua_pushboolean(st, b);
412 return *this;
413 }
414
415 Lua & push_table()
416 {
417 if (failed) return *this;
418 I(lua_checkstack (st, 1));
419 lua_newtable(st);
420 return *this;
421 }
422
423 Lua & set_table(int idx = -3)
424 {
425 if (failed) return *this;
426 I(lua_checkstack (st, 1));
427 lua_settable(st, idx);
428 return *this;
429 }
430
431
432 Lua & call(int in, int out)
433 {
434 if (failed) return *this;
435 I(lua_checkstack (st, out));
436 if (lua_pcall(st, in, out, 0) != 0)
437 {
438 I(lua_isstring (st, -1));
439 string err = string(lua_tostring(st, -1), lua_strlen(st, -1));
440 L(F("lua pcall() failed: %s\n") % err);
441 lua_pop(st, 1);
442 failed = true;
443 }
444 return *this;
445 }
446
447 Lua & pop(int count = 1)
448 {
449 if (failed) return *this;
450 if (lua_gettop (st) < count)
451 {
452 L(F("lua stack top >= count failed\n"));
453 failed = true;
454 return *this;
455 }
456 lua_pop(st, count);
457 return *this;
458 }
459};
460
461static bool
462run_string(lua_State * st, string const &str)
463{
464 I(st);
465 return
466 Lua(st)
467 .push_str("loadstring")
468 .get_fn()
469 .push_str(str)
470 .call(1,1)
471 .call(0,0)
472 .ok();
473}
474
475static bool
476run_file(lua_State * st, string const &filename)
477{
478 I(st);
479 return
480 Lua(st)
481 .push_str("loadfile")
482 .get_fn()
483 .push_str(filename)
484 .call(1,1)
485 .call(0,0)
486 .ok();
487}
488
489
490#ifdef BUILD_UNIT_TESTS
491void
492lua_hooks::add_test_hooks()
493{
494 if (!run_string(st, test_hooks_constant))
495 throw oops("lua error while setting up testing hooks");
496}
497#endif
498
499void
500lua_hooks::add_std_hooks()
501{
502 if (!run_string(st, std_hooks_constant))
503 throw oops("lua error while setting up standard hooks");
504}
505
506void
507lua_hooks::default_rcfilename(fs::path & file)
508{
509 file = mkpath(get_homedir()) / mkpath(".monotonerc");
510}
511
512void
513lua_hooks::working_copy_rcfilename(fs::path & file)
514{
515 file = mkpath(book_keeping_dir) / mkpath("monotonerc");
516}
517
518
519void
520lua_hooks::load_rcfile(fs::path const & rc, bool required)
521{
522 I(st);
523 if (fs::exists(rc))
524 {
525 L(F("opening rcfile '%s' ...\n") % rc.string());
526 N(run_file(st, rc.string()),
527 F("lua error while loading '%s'") % rc.string());
528 L(F("'%s' is ok\n") % rc.string());
529 }
530 else
531 {
532 N(!required, F("rcfile '%s' does not exist") % rc.string());
533 L(F("skipping nonexistent rcfile '%s'\n") % rc.string());
534 }
535}
536
537
538// concrete hooks
539
540// nb: if you're hooking lua to return your passphrase, you don't care if we
541// keep a couple extra temporaries of your passphrase around.
542bool
543lua_hooks::hook_get_passphrase(rsa_keypair_id const & k, string & phrase)
544{
545 return Lua(st)
546 .push_str("get_passphrase")
547 .get_fn()
548 .push_str(k())
549 .call(1,1)
550 .extract_str(phrase)
551 .ok();
552}
553
554bool
555lua_hooks::hook_persist_phrase_ok()
556{
557 bool persist_ok = false;
558 bool executed_ok = Lua(st)
559 .push_str("persist_phrase_ok")
560 .get_fn()
561 .call(0,1)
562 .extract_bool(persist_ok)
563 .ok();
564 return executed_ok && persist_ok;
565}
566
567bool
568lua_hooks::hook_expand_selector(std::string const & sel,
569 std::string & exp)
570{
571 return Lua(st)
572 .push_str("expand_selector")
573 .get_fn()
574 .push_str(sel)
575 .call(1,1)
576 .extract_str(exp)
577 .ok();
578}
579
580bool
581lua_hooks::hook_get_branch_key(cert_value const & branchname,
582 rsa_keypair_id & k)
583{
584 string key;
585 bool ok = Lua(st)
586 .push_str("get_branch_key")
587 .get_fn()
588 .push_str(branchname())
589 .call(1,1)
590 .extract_str(key)
591 .ok();
592
593 k = key;
594 return ok;
595}
596
597bool
598lua_hooks::hook_get_priv_key(rsa_keypair_id const & k,
599 base64< arc4<rsa_priv_key> > & priv_key )
600{
601 string key;
602 bool ok = Lua(st)
603 .push_str("get_priv_key")
604 .get_fn()
605 .push_str(k())
606 .call(1,1)
607 .extract_str(key)
608 .ok();
609
610 priv_key = key;
611 return ok;
612}
613
614bool
615lua_hooks::hook_get_author(cert_value const & branchname,
616 string & author)
617{
618 return Lua(st)
619 .push_str("get_author")
620 .get_fn()
621 .push_str(branchname())
622 .call(1,1)
623 .extract_str(author)
624 .ok();
625}
626
627bool
628lua_hooks::hook_edit_comment(string const & commentary,
629 string const & user_log_message,
630 string & result)
631{
632 return Lua(st)
633 .push_str("edit_comment")
634 .get_fn()
635 .push_str(commentary)
636 .push_str(user_log_message)
637 .call(2,1)
638 .extract_str(result)
639 .ok();
640}
641
642bool
643lua_hooks::hook_ignore_file(file_path const & p)
644{
645 bool ignore_it = false;
646 bool exec_ok = Lua(st)
647 .push_str("ignore_file")
648 .get_fn()
649 .push_str(p())
650 .call(1,1)
651 .extract_bool(ignore_it)
652 .ok();
653 return exec_ok && ignore_it;
654}
655
656bool
657lua_hooks::hook_ignore_branch(std::string const & branch)
658{
659 bool ignore_it = false;
660 bool exec_ok = Lua(st)
661 .push_str("ignore_branch")
662 .get_fn()
663 .push_str(branch)
664 .call(1,1)
665 .extract_bool(ignore_it)
666 .ok();
667 return exec_ok && ignore_it;
668}
669
670bool
671lua_hooks::hook_non_blocking_rng_ok()
672{
673 bool ok = false;
674 bool exec_ok = Lua(st)
675 .push_str("non_blocking_rng_ok")
676 .get_fn()
677 .call(0,1)
678 .extract_bool(ok)
679 .ok();
680 return exec_ok && ok;
681}
682
683static inline bool
684shared_trust_function_body(Lua & ll,
685 std::set<rsa_keypair_id> const & signers,
686 hexenc<id> const & id,
687 cert_name const & name,
688 cert_value const & val)
689{
690 ll.get_fn()
691 .push_table();
692
693 int k = 0;
694 for (set<rsa_keypair_id>::const_iterator v = signers.begin();
695 v != signers.end(); ++v)
696 {
697 ll.push_int(k);
698 ll.push_str((*v)());
699 ll.set_table();
700 ++k;
701 }
702
703 bool ok;
704 bool exec_ok = ll
705 .push_str(id())
706 .push_str(name())
707 .push_str(val())
708 .call(4, 1)
709 .extract_bool(ok)
710 .ok();
711
712 return exec_ok && ok;
713}
714
715bool
716lua_hooks::hook_get_revision_cert_trust(std::set<rsa_keypair_id> const & signers,
717 hexenc<id> const & id,
718 cert_name const & name,
719 cert_value const & val)
720{
721 Lua ll(st);
722 ll.push_str("get_revision_cert_trust");
723 return shared_trust_function_body(ll, signers, id, name, val);
724}
725
726bool
727lua_hooks::hook_get_manifest_cert_trust(std::set<rsa_keypair_id> const & signers,
728 hexenc<id> const & id,
729 cert_name const & name,
730 cert_value const & val)
731{
732 Lua ll(st);
733 ll.push_str("get_manifest_cert_trust");
734 return shared_trust_function_body(ll, signers, id, name, val);
735}
736
737bool
738lua_hooks::hook_accept_testresult_change(map<rsa_keypair_id, bool> const & old_results,
739 map<rsa_keypair_id, bool> const & new_results)
740{
741 Lua ll(st);
742 ll
743 .push_str("accept_testresult_change")
744 .get_fn()
745 .push_table();
746
747 for (map<rsa_keypair_id, bool>::const_iterator i = old_results.begin();
748 i != old_results.end(); ++i)
749 {
750 ll.push_str(i->first());
751 ll.push_bool(i->second);
752 ll.set_table();
753 }
754
755 ll.push_table();
756
757 for (map<rsa_keypair_id, bool>::const_iterator i = new_results.begin();
758 i != new_results.end(); ++i)
759 {
760 ll.push_str(i->first());
761 ll.push_bool(i->second);
762 ll.set_table();
763 }
764
765 bool ok;
766 bool exec_ok = ll
767 .call(2, 1)
768 .extract_bool(ok)
769 .ok();
770
771 return exec_ok && ok;
772}
773
774
775
776bool
777lua_hooks::hook_merge2(file_path const & left_path,
778 file_path const & right_path,
779 file_path const & merged_path,
780 data const & left,
781 data const & right,
782 data & result)
783{
784 string res;
785 bool ok = Lua(st)
786 .push_str("merge2")
787 .get_fn()
788 .push_str(left_path())
789 .push_str(right_path())
790 .push_str(merged_path())
791 .push_str(left())
792 .push_str(right())
793 .call(5,1)
794 .extract_str(res)
795 .ok();
796 result = res;
797 return ok;
798}
799
800bool
801lua_hooks::hook_merge3(file_path const & anc_path,
802 file_path const & left_path,
803 file_path const & right_path,
804 file_path const & merged_path,
805 data const & ancestor,
806 data const & left,
807 data const & right,
808 data & result)
809{
810 string res;
811 bool ok = Lua(st)
812 .push_str("merge3")
813 .get_fn()
814 .push_str(anc_path())
815 .push_str(left_path())
816 .push_str(right_path())
817 .push_str(merged_path())
818 .push_str(ancestor())
819 .push_str(left())
820 .push_str(right())
821 .call(7,1)
822 .extract_str(res)
823 .ok();
824 result = res;
825 return ok;
826}
827
828bool
829lua_hooks::hook_resolve_file_conflict(file_path const & anc,
830 file_path const & a,
831 file_path const & b,
832 file_path & res)
833{
834 string tmp;
835 bool ok = Lua(st)
836 .push_str("resolve_file_conflict")
837 .get_fn()
838 .push_str(anc())
839 .push_str(a())
840 .push_str(b())
841 .call(3,1)
842 .extract_str(tmp)
843 .ok();
844 res = tmp;
845 return ok;
846}
847
848bool
849lua_hooks::hook_resolve_dir_conflict(file_path const & anc,
850 file_path const & a,
851 file_path const & b,
852 file_path & res)
853{
854 string tmp;
855 bool ok = Lua(st)
856 .push_str("resolve_dir_conflict")
857 .get_fn()
858 .push_str(anc())
859 .push_str(a())
860 .push_str(b())
861 .call(3,1)
862 .extract_str(tmp)
863 .ok();
864 res = tmp;
865 return ok;
866}
867
868
869bool
870lua_hooks::hook_get_netsync_read_permitted(std::string const & collection,
871 rsa_keypair_id const & identity)
872{
873 bool permitted = false, exec_ok = false;
874
875 exec_ok = Lua(st)
876 .push_str("get_netsync_read_permitted")
877 .get_fn()
878 .push_str(collection)
879 .push_str(identity())
880 .call(2,1)
881 .extract_bool(permitted)
882 .ok();
883
884 return exec_ok && permitted;
885}
886
887bool
888lua_hooks::hook_get_netsync_anonymous_read_permitted(std::string const & collection)
889{
890 bool permitted = false, exec_ok = false;
891
892 exec_ok = Lua(st)
893 .push_str("get_netsync_anonymous_read_permitted")
894 .get_fn()
895 .push_str(collection)
896 .call(1,1)
897 .extract_bool(permitted)
898 .ok();
899
900 return exec_ok && permitted;
901}
902
903bool
904lua_hooks::hook_get_netsync_write_permitted(std::string const & collection,
905 rsa_keypair_id const & identity)
906{
907 bool permitted = false, exec_ok = false;
908
909 exec_ok = Lua(st)
910 .push_str("get_netsync_write_permitted")
911 .get_fn()
912 .push_str(collection)
913 .push_str(identity())
914 .call(2,1)
915 .extract_bool(permitted)
916 .ok();
917
918 return exec_ok && permitted;
919}
920
921
922bool
923lua_hooks::hook_apply_attribute(string const & attr,
924 file_path const & filename,
925 string const & value)
926{
927 return Lua(st)
928 .push_str("attr_functions")
929 .get_tab()
930 .push_str(attr)
931 .get_fn(-2)
932 .push_str(filename())
933 .push_str(value)
934 .call(2,0)
935 .ok();
936}
937
938
939bool
940lua_hooks::hook_get_system_linesep(string & linesep)
941{
942 return Lua(st)
943 .push_str("get_system_linesep")
944 .get_fn()
945 .call(0,1)
946 .extract_str(linesep)
947 .ok();
948}
949
950bool
951lua_hooks::hook_get_charset_conv(file_path const & p,
952 std::string & db,
953 std::string & ext)
954{
955 Lua ll(st);
956 ll
957 .push_str("get_charset_conv")
958 .get_fn()
959 .push_str(p())
960 .call(1,1)
961 .begin();
962
963 ll.next();
964 ll.extract_str(db).pop();
965
966 ll.next();
967 ll.extract_str(ext).pop();
968 return ll.ok();
969}
970
971bool
972lua_hooks::hook_get_linesep_conv(file_path const & p,
973 std::string & db,
974 std::string & ext)
975{
976 Lua ll(st);
977 ll
978 .push_str("get_linesep_conv")
979 .get_fn()
980 .push_str(p())
981 .call(1,1)
982 .begin();
983
984 ll.next();
985 ll.extract_str(db).pop();
986
987 ll.next();
988 ll.extract_str(ext).pop();
989 return ll.ok();
990}
991
992bool
993lua_hooks::hook_note_commit(revision_id const & new_id,
994 map<cert_name, cert_value> const & certs)
995{
996 Lua ll(st);
997 ll
998 .push_str("note_commit")
999 .get_fn()
1000 .push_str(new_id.inner()());
1001
1002 ll.push_table();
1003
1004 for (map<cert_name, cert_value>::const_iterator i = certs.begin();
1005 i != certs.end(); ++i)
1006 {
1007 ll.push_str(i->first());
1008 ll.push_str(i->second());
1009 ll.set_table();
1010 }
1011
1012 ll.call(2, 0);
1013 return ll.ok();
1014}

Archive Download this file

Branches

Tags

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