monotone

monotone Mtn Source Tree

Root/lua_hooks.cc

1// Copyright (C) 2002 Graydon Hoare <graydon@pobox.com>
2//
3// This program is made available under the GNU GPL version 2.0 or
4// greater. See the accompanying file COPYING for details.
5//
6// This program is distributed WITHOUT ANY WARRANTY; without even the
7// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
8// PURPOSE.
9
10
11#include "base.hh"
12#include "lua.h"
13#include "lualib.h"
14#include "lauxlib.h"
15
16#include <set>
17#include <map>
18#include <fstream>
19#include <iostream>
20
21#include "lua.hh"
22
23#include "app_state.hh"
24#include "file_io.hh"
25#include "lua_hooks.hh"
26#include "sanity.hh"
27#include "vocab.hh"
28#include "transforms.hh"
29#include "paths.hh"
30#include "uri.hh"
31#include "cmd.hh"
32#include "commands.hh"
33#include "globish.hh"
34
35// defined in {std,test}_hooks.lua, converted to {std,test}_hooks.c respectively
36extern char const std_hooks_constant[];
37#ifdef BUILD_UNIT_TESTS
38extern char const test_hooks_constant[];
39#endif
40
41using std::make_pair;
42using std::map;
43using std::pair;
44using std::set;
45using std::sort;
46using std::string;
47using std::vector;
48
49static int panic_thrower(lua_State * st)
50{
51 throw oops("lua panic");
52}
53
54// this lets the lua callbacks (monotone_*_for_lua) have access to the
55// app_state the're associated with.
56// it was added so that the confdir (normally ~/.monotone) can be specified on
57// the command line (and so known only to the app_state), and still be
58// available to lua
59// please *don't* use it for complex things that can throw errors
60static map<lua_State*, app_state*> map_of_lua_to_app;
61
62extern "C"
63{
64 static int
65 monotone_get_confdir_for_lua(lua_State *L)
66 {
67 map<lua_State*, app_state*>::iterator i = map_of_lua_to_app.find(L);
68 if (i != map_of_lua_to_app.end())
69 {
70 system_path dir = i->second->opts.conf_dir;
71 string confdir = dir.as_external();
72 lua_pushstring(L, confdir.c_str());
73 }
74 else
75 lua_pushnil(L);
76 return 1;
77 }
78}
79
80app_state*
81get_app_state(lua_State *L)
82{
83 map<lua_State*, app_state*>::iterator i = map_of_lua_to_app.find(L);
84 if (i != map_of_lua_to_app.end())
85 return i->second;
86 else
87 return NULL;
88}
89
90lua_hooks::lua_hooks()
91{
92 st = luaL_newstate();
93 I(st);
94
95 lua_atpanic (st, &panic_thrower);
96
97 luaL_openlibs(st);
98
99 lua_register(st, "get_confdir", monotone_get_confdir_for_lua);
100 add_functions(st);
101
102 // Disable any functions we don't want. This is easiest
103 // to do just by running a lua string.
104 static char const disable_dangerous[] =
105 "os.execute = function(c) "
106 " error(\"os.execute disabled for security reasons. Try spawn().\") "
107 "end "
108 "io.popen = function(c,t) "
109 " error(\"io.popen disabled for security reasons. Try spawn_pipe().\") "
110 "end ";
111
112 if (!run_string(st, disable_dangerous,
113 "<disabled dangerous functions>"))
114 throw oops("lua error while disabling existing functions");
115}
116
117lua_hooks::~lua_hooks()
118{
119 map<lua_State*, app_state*>::iterator i = map_of_lua_to_app.find(st);
120 if (st)
121 lua_close (st);
122 if (i != map_of_lua_to_app.end())
123 map_of_lua_to_app.erase(i);
124}
125
126void
127lua_hooks::set_app(app_state *_app)
128{
129 map_of_lua_to_app.insert(make_pair(st, _app));
130}
131
132bool
133lua_hooks::check_lua_state(lua_State * p_st) const
134{
135 return (p_st == st);
136}
137
138#ifdef BUILD_UNIT_TESTS
139void
140lua_hooks::add_test_hooks()
141{
142 if (!run_string(st, test_hooks_constant, "<test hooks>"))
143 throw oops("lua error while setting up testing hooks");
144}
145#endif
146
147void
148lua_hooks::add_std_hooks()
149{
150 if (!run_string(st, std_hooks_constant, "<std hooks>"))
151 throw oops("lua error while setting up standard hooks");
152}
153
154void
155lua_hooks::default_rcfilename(system_path & file)
156{
157 map<lua_State*, app_state*>::iterator i = map_of_lua_to_app.find(st);
158 I(i != map_of_lua_to_app.end());
159 file = i->second->opts.conf_dir / "monotonerc";
160}
161
162void
163lua_hooks::workspace_rcfilename(bookkeeping_path & file)
164{
165 file = bookkeeping_root / "monotonerc";
166}
167
168
169void
170lua_hooks::load_rcfile(utf8 const & rc)
171{
172 I(st);
173 if (rc() != "-" && directory_exists(system_path(rc)))
174 run_directory(st, system_path(rc).as_external().c_str(), "*");
175 else
176 {
177 data dat;
178 L(FL("opening rcfile '%s'") % rc);
179 read_data_for_command_line(rc, dat);
180 N(run_string(st, dat().c_str(), rc().c_str()),
181 F("lua error while loading rcfile '%s'") % rc);
182 L(FL("'%s' is ok") % rc);
183 }
184}
185
186void
187lua_hooks::load_rcfile(any_path const & rc, bool required)
188{
189 I(st);
190 bool exists;
191 try
192 {
193 exists = path_exists(rc);
194 }
195 catch (informative_failure & e)
196 {
197 if (!required)
198 {
199 L(FL("skipping rcfile '%s': %s") % rc % e.what());
200 return;
201 }
202 else
203 throw;
204 }
205
206 if (exists)
207 {
208 L(FL("opening rcfile '%s'") % rc);
209 N(run_file(st, rc.as_external().c_str()),
210 F("lua error while loading '%s'") % rc);
211 L(FL("'%s' is ok") % rc);
212 }
213 else
214 {
215 N(!required, F("rcfile '%s' does not exist") % rc);
216 L(FL("skipping nonexistent rcfile '%s'") % rc);
217 }
218}
219
220bool
221lua_hooks::hook_exists(std::string const & func_name)
222{
223 return Lua(st)
224 .func(func_name)
225 .ok();
226}
227
228// concrete hooks
229
230// nb: if you're hooking lua to return your passphrase, you don't care if we
231// keep a couple extra temporaries of your passphrase around.
232bool
233lua_hooks::hook_get_passphrase(rsa_keypair_id const & k, string & phrase)
234{
235 return Lua(st)
236 .func("get_passphrase")
237 .push_str(k())
238 .call(1,1)
239 .extract_classified_str(phrase)
240 .ok();
241}
242
243bool
244lua_hooks::hook_persist_phrase_ok()
245{
246 bool persist_ok = false;
247 bool executed_ok = Lua(st)
248 .func("persist_phrase_ok")
249 .call(0,1)
250 .extract_bool(persist_ok)
251 .ok();
252 return executed_ok && persist_ok;
253}
254
255bool
256lua_hooks::hook_expand_selector(string const & sel,
257 string & exp)
258{
259 return Lua(st)
260 .func("expand_selector")
261 .push_str(sel)
262 .call(1,1)
263 .extract_str(exp)
264 .ok();
265}
266
267bool
268lua_hooks::hook_expand_date(string const & sel,
269 string & exp)
270{
271 exp.clear();
272 bool res= Lua(st)
273 .func("expand_date")
274 .push_str(sel)
275 .call(1,1)
276 .extract_str(exp)
277 .ok();
278 return res && exp.size();
279}
280
281bool
282lua_hooks::hook_get_branch_key(branch_name const & branchname,
283 rsa_keypair_id & k)
284{
285 string key;
286 bool ok = Lua(st)
287 .func("get_branch_key")
288 .push_str(branchname())
289 .call(1,1)
290 .extract_str(key)
291 .ok();
292
293 k = rsa_keypair_id(key);
294 return ok;
295}
296
297bool
298lua_hooks::hook_get_author(branch_name const & branchname,
299 rsa_keypair_id const & k,
300 string & author)
301{
302 return Lua(st)
303 .func("get_author")
304 .push_str(branchname())
305 .push_str(k())
306 .call(2,1)
307 .extract_str(author)
308 .ok();
309}
310
311bool
312lua_hooks::hook_edit_comment(external const & commentary,
313 external const & user_log_message,
314 external & result)
315{
316 string result_str;
317 bool is_ok = Lua(st)
318 .func("edit_comment")
319 .push_str(commentary())
320 .push_str(user_log_message())
321 .call(2,1)
322 .extract_str(result_str)
323 .ok();
324 result = external(result_str);
325 return is_ok;
326}
327
328bool
329lua_hooks::hook_ignore_file(file_path const & p)
330{
331 bool ignore_it = false;
332 bool exec_ok = Lua(st)
333 .func("ignore_file")
334 .push_str(p.as_external())
335 .call(1,1)
336 .extract_bool(ignore_it)
337 .ok();
338 return exec_ok && ignore_it;
339}
340
341bool
342lua_hooks::hook_ignore_branch(branch_name const & branch)
343{
344 bool ignore_it = false;
345 bool exec_ok = Lua(st)
346 .func("ignore_branch")
347 .push_str(branch())
348 .call(1,1)
349 .extract_bool(ignore_it)
350 .ok();
351 return exec_ok && ignore_it;
352}
353
354static inline bool
355shared_trust_function_body(Lua & ll,
356 set<rsa_keypair_id> const & signers,
357 hexenc<id> const & id,
358 cert_name const & name,
359 cert_value const & val)
360{
361 ll.push_table();
362
363 int k = 1;
364 for (set<rsa_keypair_id>::const_iterator v = signers.begin();
365 v != signers.end(); ++v)
366 {
367 ll.push_int(k);
368 ll.push_str((*v)());
369 ll.set_table();
370 ++k;
371 }
372
373 bool ok;
374 bool exec_ok = ll
375 .push_str(id())
376 .push_str(name())
377 .push_str(val())
378 .call(4, 1)
379 .extract_bool(ok)
380 .ok();
381
382 return exec_ok && ok;
383}
384
385bool
386lua_hooks::hook_get_revision_cert_trust(set<rsa_keypair_id> const & signers,
387 hexenc<id> const & id,
388 cert_name const & name,
389 cert_value const & val)
390{
391 Lua ll(st);
392 ll.func("get_revision_cert_trust");
393 return shared_trust_function_body(ll, signers, id, name, val);
394}
395
396bool
397lua_hooks::hook_get_manifest_cert_trust(set<rsa_keypair_id> const & signers,
398 hexenc<id> const & id,
399 cert_name const & name,
400 cert_value const & val)
401{
402 Lua ll(st);
403 ll.func("get_manifest_cert_trust");
404 return shared_trust_function_body(ll, signers, id, name, val);
405}
406
407bool
408lua_hooks::hook_accept_testresult_change(map<rsa_keypair_id, bool> const & old_results,
409 map<rsa_keypair_id, bool> const & new_results)
410{
411 Lua ll(st);
412 ll
413 .func("accept_testresult_change")
414 .push_table();
415
416 for (map<rsa_keypair_id, bool>::const_iterator i = old_results.begin();
417 i != old_results.end(); ++i)
418 {
419 ll.push_str(i->first());
420 ll.push_bool(i->second);
421 ll.set_table();
422 }
423
424 ll.push_table();
425
426 for (map<rsa_keypair_id, bool>::const_iterator i = new_results.begin();
427 i != new_results.end(); ++i)
428 {
429 ll.push_str(i->first());
430 ll.push_bool(i->second);
431 ll.set_table();
432 }
433
434 bool ok;
435 bool exec_ok = ll
436 .call(2, 1)
437 .extract_bool(ok)
438 .ok();
439
440 return exec_ok && ok;
441}
442
443
444
445bool
446lua_hooks::hook_merge3(file_path const & anc_path,
447 file_path const & left_path,
448 file_path const & right_path,
449 file_path const & merged_path,
450 data const & ancestor,
451 data const & left,
452 data const & right,
453 data & result)
454{
455 string res;
456 bool ok = Lua(st)
457 .func("merge3")
458 .push_str(anc_path.as_external())
459 .push_str(left_path.as_external())
460 .push_str(right_path.as_external())
461 .push_str(merged_path.as_external())
462 .push_str(ancestor())
463 .push_str(left())
464 .push_str(right())
465 .call(7,1)
466 .extract_str(res)
467 .ok();
468 result = data(res);
469 return ok;
470}
471
472bool
473lua_hooks::hook_external_diff(file_path const & path,
474 data const & data_old,
475 data const & data_new,
476 bool is_binary,
477 bool diff_args_provided,
478 string const & diff_args,
479 string const & oldrev,
480 string const & newrev)
481{
482 Lua ll(st);
483
484 ll
485 .func("external_diff")
486 .push_str(path.as_external());
487
488 if (oldrev.length() != 0)
489 ll.push_str(data_old());
490 else
491 ll.push_nil();
492
493 ll.push_str(data_new());
494
495 ll.push_bool(is_binary);
496
497 if (diff_args_provided)
498 ll.push_str(diff_args);
499 else
500 ll.push_nil();
501
502 ll.push_str(oldrev);
503 ll.push_str(newrev);
504
505 return ll.call(7,0).ok();
506}
507
508bool
509lua_hooks::hook_get_encloser_pattern(file_path const & path,
510 std::string & pattern)
511{
512 bool exec_ok
513 = Lua(st)
514 .func("get_encloser_pattern")
515 .push_str(path.as_external())
516 .call(1, 1)
517 .extract_str(pattern)
518 .ok();
519
520 // If the hook fails, make sure pattern is set to something sane
521 // (the empty string, which will disable enclosers for this file).
522 if (!exec_ok)
523 pattern = "";
524 return exec_ok;
525}
526
527bool
528lua_hooks::hook_use_inodeprints()
529{
530 bool use = false, exec_ok = false;
531
532 exec_ok = Lua(st)
533 .func("use_inodeprints")
534 .call(0, 1)
535 .extract_bool(use)
536 .ok();
537 return use && exec_ok;
538}
539
540bool
541lua_hooks::hook_get_netsync_key(utf8 const & server_address,
542 globish const & include,
543 globish const & exclude,
544 rsa_keypair_id & k)
545{
546 string key_id;
547 bool exec_ok
548 = Lua(st)
549 .func("get_netsync_key")
550 .push_str(server_address())
551 .push_str(include())
552 .push_str(exclude())
553 .call(3, 1)
554 .extract_str(key_id)
555 .ok();
556
557 if (!exec_ok)
558 key_id = "";
559 k = rsa_keypair_id(key_id);
560 return exec_ok;
561}
562
563static void
564push_uri(uri const & u, Lua & ll)
565{
566 ll.push_table();
567
568 if (!u.scheme.empty())
569 {
570 ll.push_str("scheme");
571 ll.push_str(u.scheme);
572 ll.set_table();
573 }
574
575 if (!u.user.empty())
576 {
577 ll.push_str("user");
578 ll.push_str(u.user);
579 ll.set_table();
580 }
581
582 if (!u.host.empty())
583 {
584 ll.push_str("host");
585 ll.push_str(u.host);
586 ll.set_table();
587 }
588
589 if (!u.port.empty())
590 {
591 ll.push_str("port");
592 ll.push_str(u.port);
593 ll.set_table();
594 }
595
596 if (!u.path.empty())
597 {
598 ll.push_str("path");
599 ll.push_str(u.path);
600 ll.set_table();
601 }
602
603 if (!u.query.empty())
604 {
605 ll.push_str("query");
606 ll.push_str(u.query);
607 ll.set_table();
608 }
609
610 if (!u.fragment.empty())
611 {
612 ll.push_str("fragment");
613 ll.push_str(u.fragment);
614 ll.set_table();
615 }
616}
617
618bool
619lua_hooks::hook_get_netsync_connect_command(uri const & u,
620 globish const & include_pattern,
621 globish const & exclude_pattern,
622 bool debug,
623 std::vector<std::string> & argv)
624{
625 bool cmd = false, exec_ok = false;
626 Lua ll(st);
627 ll.func("get_netsync_connect_command");
628
629 push_uri(u, ll);
630
631 ll.push_table();
632
633 if (!include_pattern().empty())
634 {
635 ll.push_str("include");
636 ll.push_str(include_pattern());
637 ll.set_table();
638 }
639
640 if (!exclude_pattern().empty())
641 {
642 ll.push_str("exclude");
643 ll.push_str(exclude_pattern());
644 ll.set_table();
645 }
646
647 if (debug)
648 {
649 ll.push_str("debug");
650 ll.push_bool(debug);
651 ll.set_table();
652 }
653
654 ll.call(2,1);
655
656 ll.begin();
657
658 argv.clear();
659 while(ll.next())
660 {
661 std::string s;
662 ll.extract_str(s).pop();
663 argv.push_back(s);
664 }
665 return ll.ok() && !argv.empty();
666}
667
668
669bool
670lua_hooks::hook_use_transport_auth(uri const & u)
671{
672 bool use_auth = true;
673 Lua ll(st);
674 ll.func("use_transport_auth");
675 push_uri(u, ll);
676 ll.call(1,1);
677 ll.extract_bool(use_auth);
678
679 // NB: we want to return *true* here if there's a failure.
680 return use_auth;
681}
682
683
684bool
685lua_hooks::hook_get_netsync_read_permitted(string const & branch,
686 rsa_keypair_id const & identity)
687{
688 bool permitted = false, exec_ok = false;
689
690 exec_ok = Lua(st)
691 .func("get_netsync_read_permitted")
692 .push_str(branch)
693 .push_str(identity())
694 .call(2,1)
695 .extract_bool(permitted)
696 .ok();
697
698 return exec_ok && permitted;
699}
700
701// Anonymous no-key version
702bool
703lua_hooks::hook_get_netsync_read_permitted(string const & branch)
704{
705 bool permitted = false, exec_ok = false;
706
707 exec_ok = Lua(st)
708 .func("get_netsync_read_permitted")
709 .push_str(branch)
710 .push_nil()
711 .call(2,1)
712 .extract_bool(permitted)
713 .ok();
714
715 return exec_ok && permitted;
716}
717
718bool
719lua_hooks::hook_get_netsync_write_permitted(rsa_keypair_id const & identity)
720{
721 bool permitted = false, exec_ok = false;
722
723 exec_ok = Lua(st)
724 .func("get_netsync_write_permitted")
725 .push_str(identity())
726 .call(1,1)
727 .extract_bool(permitted)
728 .ok();
729
730 return exec_ok && permitted;
731}
732
733bool
734lua_hooks::hook_init_attributes(file_path const & filename,
735 map<string, string> & attrs)
736{
737 Lua ll(st);
738
739 ll
740 .push_str("attr_init_functions")
741 .get_tab();
742
743 L(FL("calling attr_init_function for %s") % filename);
744 ll.begin();
745 while (ll.next())
746 {
747 L(FL(" calling an attr_init_function for %s") % filename);
748 ll.push_str(filename.as_external());
749 ll.call(1, 1);
750
751 if (lua_isstring(st, -1))
752 {
753 string key, value;
754
755 ll.extract_str(value);
756 ll.pop();
757 ll.extract_str(key);
758
759 attrs[key] = value;
760 L(FL(" added attr %s = %s") % key % value);
761 }
762 else
763 {
764 L(FL(" no attr added"));
765 ll.pop();
766 }
767 }
768
769 return ll.pop().ok();
770}
771
772bool
773lua_hooks::hook_apply_attribute(string const & attr,
774 file_path const & filename,
775 string const & value)
776{
777 return Lua(st)
778 .push_str("attr_functions")
779 .get_tab()
780 .push_str(attr)
781 .get_fn(-2)
782 .push_str(filename.as_external())
783 .push_str(value)
784 .call(2,0)
785 .ok();
786}
787
788
789bool
790lua_hooks::hook_validate_commit_message(utf8 const & message,
791 revision_data const & new_rev,
792 branch_name const & branchname,
793 bool & validated,
794 string & reason)
795{
796 validated = true;
797 return Lua(st)
798 .func("validate_commit_message")
799 .push_str(message())
800 .push_str(new_rev.inner()())
801 .push_str(branchname())
802 .call(3, 2)
803 .extract_str(reason)
804 // XXX When validated, the extra returned string is superfluous.
805 .pop()
806 .extract_bool(validated)
807 .ok();
808}
809
810bool
811lua_hooks::hook_note_commit(revision_id const & new_id,
812 revision_data const & rdat,
813 map<cert_name, cert_value> const & certs)
814{
815 Lua ll(st);
816 ll
817 .func("note_commit")
818 .push_str(new_id.inner()())
819 .push_str(rdat.inner()());
820
821 ll.push_table();
822
823 for (map<cert_name, cert_value>::const_iterator i = certs.begin();
824 i != certs.end(); ++i)
825 {
826 ll.push_str(i->first());
827 ll.push_str(i->second());
828 ll.set_table();
829 }
830
831 ll.call(3, 0);
832 return ll.ok();
833}
834
835bool
836lua_hooks::hook_note_netsync_start(size_t session_id, string my_role,
837 int sync_type, string remote_host,
838 rsa_keypair_id remote_keyname,
839 globish include_pattern,
840 globish exclude_pattern)
841{
842 string type;
843 switch (sync_type)
844 {
845 case 1:
846 type = "push";
847 break;
848 case 2:
849 type = "pull";
850 break;
851 case 3:
852 type = "sync";
853 break;
854 default:
855 type = "unknown";
856 break;
857 }
858 Lua ll(st);
859 return ll
860 .func("note_netsync_start")
861 .push_int(session_id)
862 .push_str(my_role)
863 .push_str(type)
864 .push_str(remote_host)
865 .push_str(remote_keyname())
866 .push_str(include_pattern())
867 .push_str(exclude_pattern())
868 .call(7, 0)
869 .ok();
870}
871
872bool
873lua_hooks::hook_note_netsync_revision_received(revision_id const & new_id,
874 revision_data const & rdat,
875 set<pair<rsa_keypair_id,
876 pair<cert_name,
877 cert_value> > > const & certs,
878 size_t session_id)
879{
880 Lua ll(st);
881 ll
882 .func("note_netsync_revision_received")
883 .push_str(new_id.inner()())
884 .push_str(rdat.inner()());
885
886 ll.push_table();
887
888 typedef set<pair<rsa_keypair_id, pair<cert_name, cert_value> > > cdat;
889
890 int n = 1;
891 for (cdat::const_iterator i = certs.begin(); i != certs.end(); ++i)
892 {
893 ll.push_int(n++);
894 ll.push_table();
895 ll.push_str(i->first());
896 ll.set_field("key");
897 ll.push_str(i->second.first());
898 ll.set_field("name");
899 ll.push_str(i->second.second());
900 ll.set_field("value");
901 ll.set_table();
902 }
903
904 ll.push_int(session_id);
905 ll.call(4, 0);
906 return ll.ok();
907}
908
909bool
910lua_hooks::hook_note_netsync_pubkey_received(rsa_keypair_id const & kid,
911 size_t session_id)
912{
913 Lua ll(st);
914 ll
915 .func("note_netsync_pubkey_received")
916 .push_str(kid())
917 .push_int(session_id);
918
919 ll.call(2, 0);
920 return ll.ok();
921}
922
923bool
924lua_hooks::hook_note_netsync_cert_received(revision_id const & rid,
925 rsa_keypair_id const & kid,
926 cert_name const & name,
927 cert_value const & value,
928 size_t session_id)
929{
930 Lua ll(st);
931 ll
932 .func("note_netsync_cert_received")
933 .push_str(rid.inner()())
934 .push_str(kid())
935 .push_str(name())
936 .push_str(value())
937 .push_int(session_id);
938
939 ll.call(5, 0);
940 return ll.ok();
941}
942
943bool
944lua_hooks::hook_note_netsync_end(size_t session_id, int status,
945 size_t bytes_in, size_t bytes_out,
946 size_t certs_in, size_t certs_out,
947 size_t revs_in, size_t revs_out,
948 size_t keys_in, size_t keys_out)
949{
950 Lua ll(st);
951 return ll
952 .func("note_netsync_end")
953 .push_int(session_id)
954 .push_int(status)
955 .push_int(bytes_in)
956 .push_int(bytes_out)
957 .push_int(certs_in)
958 .push_int(certs_out)
959 .push_int(revs_in)
960 .push_int(revs_out)
961 .push_int(keys_in)
962 .push_int(keys_out)
963 .call(10, 0)
964 .ok();
965}
966
967bool
968lua_hooks::hook_note_mtn_startup(args_vector const & args)
969{
970 Lua ll(st);
971
972 ll.func("note_mtn_startup");
973
974 for (args_vector::const_iterator i = args.begin(); i != args.end(); ++i)
975 ll.push_str((*i)());
976
977 ll.call(args.size(), 0);
978 return ll.ok();
979}
980
981namespace commands {
982 class cmd_lua : public command
983 {
984 lua_State *st;
985 std::string const f_name;
986 public:
987 cmd_lua(std::string const & primary_name,
988 std::string const & params,
989 std::string const & abstract,
990 std::string const & desc,
991 lua_State *L_st,
992 std::string const & func_name) :
993 command(primary_name, "", CMD_REF(user), false, false, params,
994 abstract, desc, true, options::options_type() | options::opts::none, true),
995 st(L_st), f_name(func_name)
996 {
997 // because user commands are inserted after the normal initialisation process
998 CMD_REF(user)->children().insert(this);
999 }
1000
1001 void exec(app_state & app, command_id const & execid, args_vector const & args) const;
1002 };
1003}
1004
1005void commands::cmd_lua::exec(app_state & app,
1006 command_id const & execid,
1007 args_vector const & args) const
1008{
1009 I(st);
1010 I(app.lua.check_lua_state(st));
1011
1012 app_state* app_p = get_app_state(st);
1013 I(app_p == & app);
1014
1015 Lua ll(st);
1016 ll.func(f_name);
1017
1018 for (args_vector::const_iterator it = args.begin(); it != args.end(); ++it)
1019 ll.push_str((*it)());
1020
1021 app.mtn_automate_allowed = true;
1022
1023 ll.call(args.size(),0);
1024
1025 app.mtn_automate_allowed = false;
1026
1027 E(ll.ok(), F("Call to user command %s (lua command: %s) failed.") % primary_name() % f_name);
1028}
1029
1030LUAEXT(alias_command, )
1031{
1032 const char *old_cmd = luaL_checkstring(L, -2);
1033 const char *new_cmd = luaL_checkstring(L, -1);
1034 N(old_cmd && new_cmd,
1035 F("%s called with an invalid parameter") % "alias_command");
1036
1037 args_vector args;
1038 args.push_back(arg_type(old_cmd));
1039 commands::command_id id = commands::complete_command(args);
1040 commands::command *old_cmd_p = CMD_REF(__root__)->find_command(id);
1041
1042 old_cmd_p->add_alias(utf8(new_cmd));
1043
1044 lua_pushboolean(L, true);
1045 return 1;
1046}
1047
1048
1049LUAEXT(register_command, )
1050{
1051 const char *cmd_name = luaL_checkstring(L, -5);
1052 const char *cmd_params = luaL_checkstring(L, -4);
1053 const char *cmd_abstract = luaL_checkstring(L, -3);
1054 const char *cmd_desc = luaL_checkstring(L, -2);
1055 const char *cmd_func = luaL_checkstring(L, -1);
1056
1057 N(cmd_name && cmd_params && cmd_abstract && cmd_desc && cmd_func,
1058 F("%s called with an invalid parameter") % "register_command");
1059
1060 new commands::cmd_lua(cmd_name, cmd_params, cmd_abstract, cmd_desc, L, cmd_func); // leak this - commands can't be removed anyway
1061
1062 lua_pushboolean(L, true);
1063 return 1;
1064}
1065
1066// Local Variables:
1067// mode: C++
1068// fill-column: 76
1069// c-file-style: "gnu"
1070// indent-tabs-mode: nil
1071// End:
1072// vim: et:sw=2:sts=2:ts=2:cino=>2s,{s,\:s,+s,t0,g0,^-2,e-2,n-2,p2s,(0,=s:

Archive Download this file

Branches

Tags

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