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 if (path_exists(rc))
191 {
192 L(FL("opening rcfile '%s'") % rc);
193 N(run_file(st, rc.as_external().c_str()),
194 F("lua error while loading '%s'") % rc);
195 L(FL("'%s' is ok") % rc);
196 }
197 else
198 {
199 N(!required, F("rcfile '%s' does not exist") % rc);
200 L(FL("skipping nonexistent rcfile '%s'") % rc);
201 }
202}
203
204bool
205lua_hooks::hook_exists(std::string const & func_name)
206{
207 return Lua(st)
208 .func(func_name)
209 .ok();
210}
211
212// concrete hooks
213
214// nb: if you're hooking lua to return your passphrase, you don't care if we
215// keep a couple extra temporaries of your passphrase around.
216bool
217lua_hooks::hook_get_passphrase(rsa_keypair_id const & k, string & phrase)
218{
219 return Lua(st)
220 .func("get_passphrase")
221 .push_str(k())
222 .call(1,1)
223 .extract_classified_str(phrase)
224 .ok();
225}
226
227bool
228lua_hooks::hook_persist_phrase_ok()
229{
230 bool persist_ok = false;
231 bool executed_ok = Lua(st)
232 .func("persist_phrase_ok")
233 .call(0,1)
234 .extract_bool(persist_ok)
235 .ok();
236 return executed_ok && persist_ok;
237}
238
239bool
240lua_hooks::hook_expand_selector(string const & sel,
241 string & exp)
242{
243 return Lua(st)
244 .func("expand_selector")
245 .push_str(sel)
246 .call(1,1)
247 .extract_str(exp)
248 .ok();
249}
250
251bool
252lua_hooks::hook_expand_date(string const & sel,
253 string & exp)
254{
255 exp.clear();
256 bool res= Lua(st)
257 .func("expand_date")
258 .push_str(sel)
259 .call(1,1)
260 .extract_str(exp)
261 .ok();
262 return res && exp.size();
263}
264
265bool
266lua_hooks::hook_get_branch_key(branch_name const & branchname,
267 rsa_keypair_id & k)
268{
269 string key;
270 bool ok = Lua(st)
271 .func("get_branch_key")
272 .push_str(branchname())
273 .call(1,1)
274 .extract_str(key)
275 .ok();
276
277 k = rsa_keypair_id(key);
278 return ok;
279}
280
281bool
282lua_hooks::hook_get_author(branch_name const & branchname,
283 rsa_keypair_id const & k,
284 string & author)
285{
286 return Lua(st)
287 .func("get_author")
288 .push_str(branchname())
289 .push_str(k())
290 .call(2,1)
291 .extract_str(author)
292 .ok();
293}
294
295bool
296lua_hooks::hook_edit_comment(external const & commentary,
297 external const & user_log_message,
298 external & result)
299{
300 string result_str;
301 bool is_ok = Lua(st)
302 .func("edit_comment")
303 .push_str(commentary())
304 .push_str(user_log_message())
305 .call(2,1)
306 .extract_str(result_str)
307 .ok();
308 result = external(result_str);
309 return is_ok;
310}
311
312bool
313lua_hooks::hook_ignore_file(file_path const & p)
314{
315 bool ignore_it = false;
316 bool exec_ok = Lua(st)
317 .func("ignore_file")
318 .push_str(p.as_external())
319 .call(1,1)
320 .extract_bool(ignore_it)
321 .ok();
322 return exec_ok && ignore_it;
323}
324
325bool
326lua_hooks::hook_ignore_branch(branch_name const & branch)
327{
328 bool ignore_it = false;
329 bool exec_ok = Lua(st)
330 .func("ignore_branch")
331 .push_str(branch())
332 .call(1,1)
333 .extract_bool(ignore_it)
334 .ok();
335 return exec_ok && ignore_it;
336}
337
338static inline bool
339shared_trust_function_body(Lua & ll,
340 set<rsa_keypair_id> const & signers,
341 hexenc<id> const & id,
342 cert_name const & name,
343 cert_value const & val)
344{
345 ll.push_table();
346
347 int k = 1;
348 for (set<rsa_keypair_id>::const_iterator v = signers.begin();
349 v != signers.end(); ++v)
350 {
351 ll.push_int(k);
352 ll.push_str((*v)());
353 ll.set_table();
354 ++k;
355 }
356
357 bool ok;
358 bool exec_ok = ll
359 .push_str(id())
360 .push_str(name())
361 .push_str(val())
362 .call(4, 1)
363 .extract_bool(ok)
364 .ok();
365
366 return exec_ok && ok;
367}
368
369bool
370lua_hooks::hook_get_revision_cert_trust(set<rsa_keypair_id> const & signers,
371 hexenc<id> const & id,
372 cert_name const & name,
373 cert_value const & val)
374{
375 Lua ll(st);
376 ll.func("get_revision_cert_trust");
377 return shared_trust_function_body(ll, signers, id, name, val);
378}
379
380bool
381lua_hooks::hook_get_manifest_cert_trust(set<rsa_keypair_id> const & signers,
382 hexenc<id> const & id,
383 cert_name const & name,
384 cert_value const & val)
385{
386 Lua ll(st);
387 ll.func("get_manifest_cert_trust");
388 return shared_trust_function_body(ll, signers, id, name, val);
389}
390
391bool
392lua_hooks::hook_accept_testresult_change(map<rsa_keypair_id, bool> const & old_results,
393 map<rsa_keypair_id, bool> const & new_results)
394{
395 Lua ll(st);
396 ll
397 .func("accept_testresult_change")
398 .push_table();
399
400 for (map<rsa_keypair_id, bool>::const_iterator i = old_results.begin();
401 i != old_results.end(); ++i)
402 {
403 ll.push_str(i->first());
404 ll.push_bool(i->second);
405 ll.set_table();
406 }
407
408 ll.push_table();
409
410 for (map<rsa_keypair_id, bool>::const_iterator i = new_results.begin();
411 i != new_results.end(); ++i)
412 {
413 ll.push_str(i->first());
414 ll.push_bool(i->second);
415 ll.set_table();
416 }
417
418 bool ok;
419 bool exec_ok = ll
420 .call(2, 1)
421 .extract_bool(ok)
422 .ok();
423
424 return exec_ok && ok;
425}
426
427
428
429bool
430lua_hooks::hook_merge3(file_path const & anc_path,
431 file_path const & left_path,
432 file_path const & right_path,
433 file_path const & merged_path,
434 data const & ancestor,
435 data const & left,
436 data const & right,
437 data & result)
438{
439 string res;
440 bool ok = Lua(st)
441 .func("merge3")
442 .push_str(anc_path.as_external())
443 .push_str(left_path.as_external())
444 .push_str(right_path.as_external())
445 .push_str(merged_path.as_external())
446 .push_str(ancestor())
447 .push_str(left())
448 .push_str(right())
449 .call(7,1)
450 .extract_str(res)
451 .ok();
452 result = data(res);
453 return ok;
454}
455
456bool
457lua_hooks::hook_external_diff(file_path const & path,
458 data const & data_old,
459 data const & data_new,
460 bool is_binary,
461 bool diff_args_provided,
462 string const & diff_args,
463 string const & oldrev,
464 string const & newrev)
465{
466 Lua ll(st);
467
468 ll
469 .func("external_diff")
470 .push_str(path.as_external());
471
472 if (oldrev.length() != 0)
473 ll.push_str(data_old());
474 else
475 ll.push_nil();
476
477 ll.push_str(data_new());
478
479 ll.push_bool(is_binary);
480
481 if (diff_args_provided)
482 ll.push_str(diff_args);
483 else
484 ll.push_nil();
485
486 ll.push_str(oldrev);
487 ll.push_str(newrev);
488
489 return ll.call(7,0).ok();
490}
491
492bool
493lua_hooks::hook_get_encloser_pattern(file_path const & path,
494 std::string & pattern)
495{
496 bool exec_ok
497 = Lua(st)
498 .func("get_encloser_pattern")
499 .push_str(path.as_external())
500 .call(1, 1)
501 .extract_str(pattern)
502 .ok();
503
504 // If the hook fails, make sure pattern is set to something sane
505 // (the empty string, which will disable enclosers for this file).
506 if (!exec_ok)
507 pattern = "";
508 return exec_ok;
509}
510
511bool
512lua_hooks::hook_use_inodeprints()
513{
514 bool use = false, exec_ok = false;
515
516 exec_ok = Lua(st)
517 .func("use_inodeprints")
518 .call(0, 1)
519 .extract_bool(use)
520 .ok();
521 return use && exec_ok;
522}
523
524bool
525lua_hooks::hook_get_netsync_key(utf8 const & server_address,
526 globish const & include,
527 globish const & exclude,
528 rsa_keypair_id & k)
529{
530 string key_id;
531 bool exec_ok
532 = Lua(st)
533 .func("get_netsync_key")
534 .push_str(server_address())
535 .push_str(include())
536 .push_str(exclude())
537 .call(3, 1)
538 .extract_str(key_id)
539 .ok();
540
541 if (!exec_ok)
542 key_id = "";
543 k = rsa_keypair_id(key_id);
544 return exec_ok;
545}
546
547static void
548push_uri(uri const & u, Lua & ll)
549{
550 ll.push_table();
551
552 if (!u.scheme.empty())
553 {
554 ll.push_str("scheme");
555 ll.push_str(u.scheme);
556 ll.set_table();
557 }
558
559 if (!u.user.empty())
560 {
561 ll.push_str("user");
562 ll.push_str(u.user);
563 ll.set_table();
564 }
565
566 if (!u.host.empty())
567 {
568 ll.push_str("host");
569 ll.push_str(u.host);
570 ll.set_table();
571 }
572
573 if (!u.port.empty())
574 {
575 ll.push_str("port");
576 ll.push_str(u.port);
577 ll.set_table();
578 }
579
580 if (!u.path.empty())
581 {
582 ll.push_str("path");
583 ll.push_str(u.path);
584 ll.set_table();
585 }
586
587 if (!u.query.empty())
588 {
589 ll.push_str("query");
590 ll.push_str(u.query);
591 ll.set_table();
592 }
593
594 if (!u.fragment.empty())
595 {
596 ll.push_str("fragment");
597 ll.push_str(u.fragment);
598 ll.set_table();
599 }
600}
601
602bool
603lua_hooks::hook_get_netsync_connect_command(uri const & u,
604 globish const & include_pattern,
605 globish const & exclude_pattern,
606 bool debug,
607 std::vector<std::string> & argv)
608{
609 bool cmd = false, exec_ok = false;
610 Lua ll(st);
611 ll.func("get_netsync_connect_command");
612
613 push_uri(u, ll);
614
615 ll.push_table();
616
617 if (!include_pattern().empty())
618 {
619 ll.push_str("include");
620 ll.push_str(include_pattern());
621 ll.set_table();
622 }
623
624 if (!exclude_pattern().empty())
625 {
626 ll.push_str("exclude");
627 ll.push_str(exclude_pattern());
628 ll.set_table();
629 }
630
631 if (debug)
632 {
633 ll.push_str("debug");
634 ll.push_bool(debug);
635 ll.set_table();
636 }
637
638 ll.call(2,1);
639
640 ll.begin();
641
642 argv.clear();
643 while(ll.next())
644 {
645 std::string s;
646 ll.extract_str(s).pop();
647 argv.push_back(s);
648 }
649 return ll.ok() && !argv.empty();
650}
651
652
653bool
654lua_hooks::hook_use_transport_auth(uri const & u)
655{
656 bool use_auth = true;
657 Lua ll(st);
658 ll.func("use_transport_auth");
659 push_uri(u, ll);
660 ll.call(1,1);
661 ll.extract_bool(use_auth);
662
663 // NB: we want to return *true* here if there's a failure.
664 return use_auth;
665}
666
667
668bool
669lua_hooks::hook_get_netsync_read_permitted(string const & branch,
670 rsa_keypair_id const & identity)
671{
672 bool permitted = false, exec_ok = false;
673
674 exec_ok = Lua(st)
675 .func("get_netsync_read_permitted")
676 .push_str(branch)
677 .push_str(identity())
678 .call(2,1)
679 .extract_bool(permitted)
680 .ok();
681
682 return exec_ok && permitted;
683}
684
685// Anonymous no-key version
686bool
687lua_hooks::hook_get_netsync_read_permitted(string const & branch)
688{
689 bool permitted = false, exec_ok = false;
690
691 exec_ok = Lua(st)
692 .func("get_netsync_read_permitted")
693 .push_str(branch)
694 .push_nil()
695 .call(2,1)
696 .extract_bool(permitted)
697 .ok();
698
699 return exec_ok && permitted;
700}
701
702bool
703lua_hooks::hook_get_netsync_write_permitted(rsa_keypair_id const & identity)
704{
705 bool permitted = false, exec_ok = false;
706
707 exec_ok = Lua(st)
708 .func("get_netsync_write_permitted")
709 .push_str(identity())
710 .call(1,1)
711 .extract_bool(permitted)
712 .ok();
713
714 return exec_ok && permitted;
715}
716
717bool
718lua_hooks::hook_init_attributes(file_path const & filename,
719 map<string, string> & attrs)
720{
721 Lua ll(st);
722
723 ll
724 .push_str("attr_init_functions")
725 .get_tab();
726
727 L(FL("calling attr_init_function for %s") % filename);
728 ll.begin();
729 while (ll.next())
730 {
731 L(FL(" calling an attr_init_function for %s") % filename);
732 ll.push_str(filename.as_external());
733 ll.call(1, 1);
734
735 if (lua_isstring(st, -1))
736 {
737 string key, value;
738
739 ll.extract_str(value);
740 ll.pop();
741 ll.extract_str(key);
742
743 attrs[key] = value;
744 L(FL(" added attr %s = %s") % key % value);
745 }
746 else
747 {
748 L(FL(" no attr added"));
749 ll.pop();
750 }
751 }
752
753 return ll.pop().ok();
754}
755
756bool
757lua_hooks::hook_apply_attribute(string const & attr,
758 file_path const & filename,
759 string const & value)
760{
761 return Lua(st)
762 .push_str("attr_functions")
763 .get_tab()
764 .push_str(attr)
765 .get_fn(-2)
766 .push_str(filename.as_external())
767 .push_str(value)
768 .call(2,0)
769 .ok();
770}
771
772
773bool
774lua_hooks::hook_validate_commit_message(utf8 const & message,
775 revision_data const & new_rev,
776 branch_name const & branchname,
777 bool & validated,
778 string & reason)
779{
780 validated = true;
781 return Lua(st)
782 .func("validate_commit_message")
783 .push_str(message())
784 .push_str(new_rev.inner()())
785 .push_str(branchname())
786 .call(3, 2)
787 .extract_str(reason)
788 // XXX When validated, the extra returned string is superfluous.
789 .pop()
790 .extract_bool(validated)
791 .ok();
792}
793
794bool
795lua_hooks::hook_note_commit(revision_id const & new_id,
796 revision_data const & rdat,
797 map<cert_name, cert_value> const & certs)
798{
799 Lua ll(st);
800 ll
801 .func("note_commit")
802 .push_str(new_id.inner()())
803 .push_str(rdat.inner()());
804
805 ll.push_table();
806
807 for (map<cert_name, cert_value>::const_iterator i = certs.begin();
808 i != certs.end(); ++i)
809 {
810 ll.push_str(i->first());
811 ll.push_str(i->second());
812 ll.set_table();
813 }
814
815 ll.call(3, 0);
816 return ll.ok();
817}
818
819bool
820lua_hooks::hook_note_netsync_start(size_t session_id, string my_role,
821 int sync_type, string remote_host,
822 rsa_keypair_id remote_keyname,
823 globish include_pattern,
824 globish exclude_pattern)
825{
826 string type;
827 switch (sync_type)
828 {
829 case 1:
830 type = "push";
831 break;
832 case 2:
833 type = "pull";
834 break;
835 case 3:
836 type = "sync";
837 break;
838 default:
839 type = "unknown";
840 break;
841 }
842 Lua ll(st);
843 return ll
844 .func("note_netsync_start")
845 .push_int(session_id)
846 .push_str(my_role)
847 .push_str(type)
848 .push_str(remote_host)
849 .push_str(remote_keyname())
850 .push_str(include_pattern())
851 .push_str(exclude_pattern())
852 .call(7, 0)
853 .ok();
854}
855
856bool
857lua_hooks::hook_note_netsync_revision_received(revision_id const & new_id,
858 revision_data const & rdat,
859 set<pair<rsa_keypair_id,
860 pair<cert_name,
861 cert_value> > > const & certs,
862 size_t session_id)
863{
864 Lua ll(st);
865 ll
866 .func("note_netsync_revision_received")
867 .push_str(new_id.inner()())
868 .push_str(rdat.inner()());
869
870 ll.push_table();
871
872 typedef set<pair<rsa_keypair_id, pair<cert_name, cert_value> > > cdat;
873
874 int n = 1;
875 for (cdat::const_iterator i = certs.begin(); i != certs.end(); ++i)
876 {
877 ll.push_int(n++);
878 ll.push_table();
879 ll.push_str(i->first());
880 ll.set_field("key");
881 ll.push_str(i->second.first());
882 ll.set_field("name");
883 ll.push_str(i->second.second());
884 ll.set_field("value");
885 ll.set_table();
886 }
887
888 ll.push_int(session_id);
889 ll.call(4, 0);
890 return ll.ok();
891}
892
893bool
894lua_hooks::hook_note_netsync_pubkey_received(rsa_keypair_id const & kid,
895 size_t session_id)
896{
897 Lua ll(st);
898 ll
899 .func("note_netsync_pubkey_received")
900 .push_str(kid())
901 .push_int(session_id);
902
903 ll.call(2, 0);
904 return ll.ok();
905}
906
907bool
908lua_hooks::hook_note_netsync_cert_received(revision_id const & rid,
909 rsa_keypair_id const & kid,
910 cert_name const & name,
911 cert_value const & value,
912 size_t session_id)
913{
914 Lua ll(st);
915 ll
916 .func("note_netsync_cert_received")
917 .push_str(rid.inner()())
918 .push_str(kid())
919 .push_str(name())
920 .push_str(value())
921 .push_int(session_id);
922
923 ll.call(5, 0);
924 return ll.ok();
925}
926
927bool
928lua_hooks::hook_note_netsync_end(size_t session_id, int status,
929 size_t bytes_in, size_t bytes_out,
930 size_t certs_in, size_t certs_out,
931 size_t revs_in, size_t revs_out,
932 size_t keys_in, size_t keys_out)
933{
934 Lua ll(st);
935 return ll
936 .func("note_netsync_end")
937 .push_int(session_id)
938 .push_int(status)
939 .push_int(bytes_in)
940 .push_int(bytes_out)
941 .push_int(certs_in)
942 .push_int(certs_out)
943 .push_int(revs_in)
944 .push_int(revs_out)
945 .push_int(keys_in)
946 .push_int(keys_out)
947 .call(10, 0)
948 .ok();
949}
950
951bool
952lua_hooks::hook_note_mtn_startup(args_vector const & args)
953{
954 Lua ll(st);
955
956 ll.func("note_mtn_startup");
957
958 for (args_vector::const_iterator i = args.begin(); i != args.end(); ++i)
959 ll.push_str((*i)());
960
961 ll.call(args.size(), 0);
962 return ll.ok();
963}
964
965namespace commands {
966 class cmd_lua : public command
967 {
968 lua_State *st;
969 std::string const f_name;
970 public:
971 cmd_lua(std::string const & primary_name,
972 std::string const & params,
973 std::string const & abstract,
974 std::string const & desc,
975 lua_State *L_st,
976 std::string const & func_name) :
977 command(primary_name, "", CMD_REF(user), false, false, params,
978 abstract, desc, true, options::options_type() | options::opts::none, true),
979 st(L_st), f_name(func_name)
980 {
981 // because user commands are inserted after the normal initialisation process
982 CMD_REF(user)->children().insert(this);
983 }
984
985 void exec(app_state & app, command_id const & execid, args_vector const & args) const;
986 };
987}
988
989void commands::cmd_lua::exec(app_state & app,
990 command_id const & execid,
991 args_vector const & args) const
992{
993 I(st);
994 I(app.lua.check_lua_state(st));
995
996 app_state* app_p = get_app_state(st);
997 I(app_p == & app);
998
999 Lua ll(st);
1000 ll.func(f_name);
1001
1002 for (args_vector::const_iterator it = args.begin(); it != args.end(); ++it)
1003 ll.push_str((*it)());
1004
1005 app.mtn_automate_allowed = true;
1006
1007 ll.call(args.size(),0);
1008
1009 app.mtn_automate_allowed = false;
1010
1011 E(ll.ok(), F("Call to user command %s (lua command: %s) failed.") % primary_name() % f_name);
1012}
1013
1014LUAEXT(alias_command, )
1015{
1016 const char *old_cmd = luaL_checkstring(L, -2);
1017 const char *new_cmd = luaL_checkstring(L, -1);
1018 N(old_cmd && new_cmd,
1019 F("%s called with an invalid parameter") % "alias_command");
1020
1021 args_vector args;
1022 args.push_back(arg_type(old_cmd));
1023 commands::command_id id = commands::complete_command(args);
1024 commands::command *old_cmd_p = CMD_REF(__root__)->find_command(id);
1025
1026 old_cmd_p->add_alias(utf8(new_cmd));
1027
1028 lua_pushboolean(L, true);
1029 return 1;
1030}
1031
1032
1033LUAEXT(register_command, )
1034{
1035 const char *cmd_name = luaL_checkstring(L, -5);
1036 const char *cmd_params = luaL_checkstring(L, -4);
1037 const char *cmd_abstract = luaL_checkstring(L, -3);
1038 const char *cmd_desc = luaL_checkstring(L, -2);
1039 const char *cmd_func = luaL_checkstring(L, -1);
1040
1041 N(cmd_name && cmd_params && cmd_abstract && cmd_desc && cmd_func,
1042 F("%s called with an invalid parameter") % "register_command");
1043
1044 new commands::cmd_lua(cmd_name, cmd_params, cmd_abstract, cmd_desc, L, cmd_func); // leak this - commands can't be removed anyway
1045
1046 lua_pushboolean(L, true);
1047 return 1;
1048}
1049
1050// Local Variables:
1051// mode: C++
1052// fill-column: 76
1053// c-file-style: "gnu"
1054// indent-tabs-mode: nil
1055// End:
1056// 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