config: Access OPT_MUST_SAVE in the real option, not alias.
[elinks/elinks-j605.git] / src / scripting / lua / core.c
blobd720738740ccda89bf62825f68df683607061734
1 /* Lua interface (scripting engine) */
3 #ifdef HAVE_CONFIG_H
4 #include "config.h"
5 #endif
7 #include <setjmp.h>
8 #include <signal.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #ifdef HAVE_UNISTD_H
12 #include <unistd.h>
13 #endif
15 #include <lua.h>
16 #include <lualib.h>
18 #include "elinks.h"
20 #include "bfu/dialog.h"
21 #include "cache/cache.h"
22 #include "config/home.h"
23 #include "config/kbdbind.h"
24 #include "config/options.h"
25 #include "config/opttypes.h"
26 #include "document/document.h"
27 #include "document/renderer.h"
28 #include "document/view.h"
29 #include "intl/charsets.h"
30 #include "intl/gettext/libintl.h"
31 #include "main/event.h"
32 #include "main/module.h"
33 #include "osdep/signals.h"
34 #include "protocol/uri.h"
35 #include "scripting/lua/core.h"
36 #include "session/location.h"
37 #include "session/session.h"
38 #include "session/task.h"
39 #include "terminal/terminal.h"
40 #include "util/color.h"
41 #include "util/conv.h"
42 #include "util/file.h"
43 #include "util/memory.h"
44 #include "util/string.h"
45 #include "viewer/dump/dump.h"
46 #include "viewer/text/view.h"
47 #include "viewer/text/vs.h"
49 #define LUA_HOOKS_FILENAME "hooks.lua"
52 lua_State *lua_state;
54 static struct session *lua_ses;
55 static struct terminal *errterm;
56 static sigjmp_buf errjmp;
58 #define L lua_state
59 #define LS lua_State *S
61 static void handle_standard_lua_returns(unsigned char *from);
62 static void handle_ref(LS, struct session *ses, int func_ref,
63 unsigned char *from, int num_args, int unref);
67 * Functions exported to the lua_State.
70 static int
71 l_alert(LS)
73 unsigned char *msg = (unsigned char *) lua_tostring(S, 1);
75 /* Don't crash if a script calls e.g. error(nil) or error(error). */
76 if (msg == NULL)
77 msg = "(cannot convert the error message to a string)";
79 alert_lua_error(msg);
80 return 0;
83 static int
84 l_current_url(LS)
86 if (lua_ses && have_location(lua_ses)) {
87 struct view_state *vs = &cur_loc(lua_ses)->vs;
88 unsigned char *url = get_uri_string(vs->uri, URI_ORIGINAL);
90 if (url) {
91 lua_pushstring(S, url);
92 mem_free(url);
93 return 1;
97 lua_pushnil(S);
98 return 1;
101 static int
102 l_current_link(LS)
104 struct link *link = get_current_session_link(lua_ses);
106 if (link) {
107 lua_pushstring(S, link->where);
108 } else {
109 lua_pushnil(S);
112 return 1;
115 static int
116 l_current_title(LS)
118 struct document_view *doc_view = current_frame(lua_ses);
120 if (doc_view && doc_view->document->title) {
121 unsigned char *clean_title = stracpy(doc_view->document->title);
123 if (clean_title) {
124 sanitize_title(clean_title);
126 lua_pushstring(S, clean_title);
127 mem_free(clean_title);
128 return 1;
132 lua_pushnil(S);
133 return 1;
136 static int
137 l_current_document(LS)
139 if (lua_ses) {
140 struct cache_entry *cached = find_in_cache(cur_loc(lua_ses)->vs.uri);
141 struct fragment *f = cached ? cached->frag.next : NULL;
143 if (f && f->length) {
144 lua_pushlstring(S, f->data, f->length);
145 return 1;
149 lua_pushnil(S);
150 return 1;
153 /* XXX: This function is mostly copied from `dump_to_file'. */
154 static int
155 l_current_document_formatted(LS)
157 struct document_view *doc_view;
158 struct string buffer;
159 int width, old_width = 0;
161 if (lua_gettop(S) == 0) width = -1;
162 else if (!lua_isnumber(S, 1)) goto lua_error;
163 else if ((width = lua_tonumber(S, 1)) <= 0) goto lua_error;
165 if (!lua_ses || !(doc_view = current_frame(lua_ses))) goto lua_error;
166 if (width > 0) {
167 old_width = lua_ses->tab->term->width;
168 lua_ses->tab->term->width = width;
169 render_document_frames(lua_ses, 0);
172 if (init_string(&buffer)) {
173 add_document_to_string(&buffer, doc_view->document);
174 lua_pushlstring(S, buffer.source, buffer.length);
175 done_string(&buffer);
178 if (width > 0) {
179 lua_ses->tab->term->width = old_width;
180 render_document_frames(lua_ses, 0);
182 return 1;
184 lua_error:
185 lua_pushnil(S);
186 return 1;
189 static int
190 l_pipe_read(LS)
192 FILE *fp;
193 unsigned char *s = NULL;
194 size_t len = 0;
196 if (!lua_isstring(S, 1)) goto lua_error;
198 fp = popen(lua_tostring(S, 1), "r");
199 if (!fp) goto lua_error;
201 while (!feof(fp)) {
202 unsigned char buf[1024];
203 size_t l = fread(buf, 1, sizeof(buf), fp);
205 if (l > 0) {
206 unsigned char *news = mem_realloc(s, len + l);
208 if (!news) goto lua_error;
209 s = news;
210 memcpy(s + len, buf, l);
211 len += l;
213 } else if (l < 0) {
214 goto lua_error;
217 pclose(fp);
219 lua_pushlstring(S, s, len);
220 mem_free_if(s);
221 return 1;
223 lua_error:
224 mem_free_if(s);
225 lua_pushnil(S);
226 return 1;
229 static int
230 l_execute(LS)
232 if (lua_isstring(S, 1)) {
233 exec_on_terminal(lua_ses->tab->term, (unsigned char *) lua_tostring(S, 1), "", 0);
234 lua_pushnumber(S, 0);
235 return 1;
238 lua_pushnil(L);
239 return 1;
242 static int
243 l_tmpname(LS)
245 unsigned char *fn = tempnam(NULL, "elinks");
247 if (fn) {
248 lua_pushstring(S, fn);
249 free(fn);
250 return 1;
253 alert_lua_error("Error generating temporary file name");
254 lua_pushnil(S);
255 return 1;
259 * Helper to run Lua functions bound to keystrokes.
262 static enum evhook_status
263 run_lua_func(va_list ap, void *data)
265 struct session *ses = va_arg(ap, struct session *);
266 int func_ref = (long) data;
268 if (func_ref == LUA_NOREF) {
269 alert_lua_error("key bound to nothing (internal error)");
270 return EVENT_HOOK_STATUS_NEXT;
273 handle_ref(L, ses, func_ref, "keyboard function", 0, 0);
275 return EVENT_HOOK_STATUS_NEXT;
278 static int
279 l_bind_key(LS)
281 int ref;
282 int event_id;
283 unsigned char *err = NULL;
284 struct string event_name = NULL_STRING;
286 if (!lua_isstring(S, 1) || !lua_isstring(S, 2)
287 || !lua_isfunction(S, 3)) {
288 alert_lua_error("bad arguments to bind_key");
289 goto lua_error;
292 if (!init_string(&event_name)) goto lua_error;
294 lua_pushvalue(S, 3);
295 ref = luaL_ref(S, LUA_REGISTRYINDEX);
296 add_format_to_string(&event_name, "lua-run-func %i", ref);
298 event_id = bind_key_to_event_name((unsigned char *) lua_tostring(S, 1),
299 (unsigned char *) lua_tostring(S, 2),
300 event_name.source, &err);
301 done_string(&event_name);
303 if (!err) {
304 event_id = register_event_hook(event_id, run_lua_func, 0,
305 (void *) (long) ref);
307 if (event_id == EVENT_NONE)
308 err = gettext("Error registering event hook");
311 if (err) {
312 luaL_unref(S, LUA_REGISTRYINDEX, ref);
313 alert_lua_error2("error in bind_key: ", err);
314 goto lua_error;
317 lua_pushnumber(S, 1);
318 return 1;
320 lua_error:
321 lua_pushnil(S);
322 return 1;
326 /* Begin very hackish bit for bookmark editing dialog. */
327 /* XXX: Add history and generalise. */
329 struct lua_dlg_data {
330 lua_State *state;
331 unsigned char cat[MAX_STR_LEN];
332 unsigned char name[MAX_STR_LEN];
333 unsigned char url[MAX_STR_LEN];
334 int func_ref;
337 static void
338 dialog_run_lua(void *data_)
340 struct lua_dlg_data *data = data_;
341 lua_State *s = data->state;
343 lua_pushstring(s, data->cat);
344 lua_pushstring(s, data->name);
345 lua_pushstring(s, data->url);
346 handle_ref(s, lua_ses, data->func_ref, "post dialog function", 3, 1);
349 static int
350 l_edit_bookmark_dialog(LS)
352 struct terminal *term = lua_ses->tab->term;
353 struct dialog *dlg;
354 struct lua_dlg_data *data;
356 if (!lua_isstring(S, 1) || !lua_isstring(S, 2)
357 || !lua_isstring(S, 3) || !lua_isfunction(S, 4)) {
358 lua_pushnil(S);
359 return 1;
362 #define L_EDIT_BMK_WIDGETS_COUNT 5
363 dlg = calloc_dialog(L_EDIT_BMK_WIDGETS_COUNT, sizeof(*data));
364 if (!dlg) return 0;
366 data = (struct lua_dlg_data *) get_dialog_offset(dlg, L_EDIT_BMK_WIDGETS_COUNT);
367 data->state = S;
368 safe_strncpy(data->cat, (unsigned char *) lua_tostring(S, 1),
369 MAX_STR_LEN-1);
370 safe_strncpy(data->name, (unsigned char *) lua_tostring(S, 2),
371 MAX_STR_LEN-1);
372 safe_strncpy(data->url, (unsigned char *) lua_tostring(S, 3),
373 MAX_STR_LEN-1);
374 lua_pushvalue(S, 4);
375 data->func_ref = luaL_ref(S, LUA_REGISTRYINDEX);
377 dlg->title = _("Edit bookmark", term);
378 dlg->layouter = generic_dialog_layouter;
379 dlg->layout.maximize_width = 1;
381 add_dlg_field(dlg, _("Name", term), 0, 0, NULL, MAX_STR_LEN, data->cat, NULL);
382 add_dlg_field(dlg, _("Name", term), 0, 0, NULL, MAX_STR_LEN, data->name, NULL);
383 add_dlg_field(dlg, _("URL", term), 0, 0, NULL, MAX_STR_LEN, data->url, NULL);
385 add_dlg_ok_button(dlg, _("~OK", term), B_ENTER, dialog_run_lua, data);
386 add_dlg_button(dlg, _("~Cancel", term), B_ESC, cancel_dialog, NULL);
388 add_dlg_end(dlg, L_EDIT_BMK_WIDGETS_COUNT);
390 do_dialog(term, dlg, getml(dlg, NULL));
392 lua_pushnumber(S, 1);
393 return 1;
396 /* End very hackish bit. */
399 /* Begin hackish bit for half-generalised dialog. */
400 /* XXX: Add history and custom labels. */
402 #define XDIALOG_MAX_FIELDS 5
404 struct lua_xdialog_data {
405 lua_State *state;
406 int func_ref;
407 int nfields;
408 unsigned char fields[XDIALOG_MAX_FIELDS][MAX_STR_LEN];
411 static void
412 xdialog_run_lua(void *data_)
414 struct lua_xdialog_data *data = data_;
415 lua_State *s = data->state;
416 int i;
418 for (i = 0; i < data->nfields; i++) lua_pushstring(s, data->fields[i]);
419 handle_ref(s, lua_ses, data->func_ref, "post xdialog function",
420 data->nfields, 1);
423 static int
424 l_xdialog(LS)
426 struct terminal *term;
427 struct dialog *dlg;
428 struct lua_xdialog_data *data;
429 int nargs, nfields, nitems;
430 int i = 0;
432 if (!lua_ses) return 0;
434 term = lua_ses->tab->term;
436 nargs = lua_gettop(S);
437 nfields = nargs - 1;
438 nitems = nfields + 2;
440 if ((nfields < 1) || (nfields > XDIALOG_MAX_FIELDS)) goto lua_error;
441 for (i = 1; i < nargs; i++) if (!lua_isstring(S, i)) goto lua_error;
442 if (!lua_isfunction(S, nargs)) goto lua_error;
444 dlg = calloc_dialog(nitems, sizeof(*data));
445 if (!dlg) return 0;
447 data = (struct lua_xdialog_data *) get_dialog_offset(dlg, nitems);
448 data->state = S;
449 data->nfields = nfields;
450 for (i = 0; i < nfields; i++)
451 safe_strncpy(data->fields[i],
452 (unsigned char *) lua_tostring(S, i+1),
453 MAX_STR_LEN-1);
454 lua_pushvalue(S, nargs);
455 data->func_ref = luaL_ref(S, LUA_REGISTRYINDEX);
457 dlg->title = _("User dialog", term);
458 dlg->layouter = generic_dialog_layouter;
459 dlg->layout.maximize_width = 1;
461 for (i = 0; i < nfields; i++)
462 add_dlg_field(dlg, _("Name", term), 0, 0, NULL, MAX_STR_LEN,
463 data->fields[i], NULL);
465 add_dlg_ok_button(dlg, _("~OK", term), B_ENTER, xdialog_run_lua, data);
466 add_dlg_button(dlg, _("~Cancel", term), B_ESC, cancel_dialog, NULL);
468 add_dlg_end(dlg, nitems);
470 do_dialog(term, dlg, getml(dlg, NULL));
472 lua_pushnumber(S, 1);
473 return 1;
475 lua_error:
476 lua_pushnil(S);
477 return 1;
480 /* End xdialog bit. */
482 /* Set/get option */
484 static int
485 l_set_option(LS)
487 int nargs;
488 struct option *opt, *current;
489 const char *name;
491 nargs = lua_gettop(S);
492 if (nargs != 2)
493 goto lua_error;
495 /* Get option record */
496 name = lua_tostring(S, 1);
497 opt = get_opt_rec(config_options, (unsigned char *) name);
498 if (opt == NULL)
499 goto lua_error;
501 /* Set option */
502 switch (opt->type) {
503 case OPT_BOOL:
505 int value;
507 value = lua_toboolean(S, 2);
508 option_types[opt->type].set(opt, (unsigned char *) (&value));
509 break;
512 case OPT_INT:
513 case OPT_LONG:
515 int value;
517 value = lua_tonumber(S, 2);
518 option_types[opt->type].set(opt, (unsigned char *) (&value));
519 break;
521 case OPT_STRING:
522 case OPT_CODEPAGE:
523 case OPT_LANGUAGE:
524 case OPT_COLOR:
525 option_types[opt->type].set(opt, (unsigned char *) lua_tostring(S, 2));
526 break;
527 default:
528 goto lua_error;
531 opt->flags |= OPT_TOUCHED;
533 /* Call hook */
534 current = opt;
535 call_change_hooks(lua_ses, current, opt);
536 return 1;
538 lua_error:
539 lua_pushnil(S);
540 return 1;
543 static int
544 l_get_option(LS)
546 int nargs;
547 struct option *opt;
548 const char *name;
550 /* Get option record */
551 nargs = lua_gettop(S);
552 if (nargs != 1)
553 goto lua_error;
554 name = lua_tostring(S, 1);
555 opt = get_opt_rec(config_options, (unsigned char *) name);
556 if (opt == NULL)
557 goto lua_error;
559 /* Convert to an appropriate Lua type */
560 switch (opt->type) {
561 case OPT_BOOL:
562 lua_pushboolean(S, opt->value.number);
563 break;
564 case OPT_INT:
565 case OPT_LONG:
566 lua_pushnumber(S, opt->value.number);
567 break;
568 case OPT_STRING:
569 lua_pushstring(S, opt->value.string);
570 break;
571 case OPT_CODEPAGE:
573 unsigned char *cp_name;
575 cp_name = get_cp_mime_name(opt->value.number);
576 lua_pushstring(S, cp_name);
577 break;
579 case OPT_LANGUAGE:
581 unsigned char *lang;
583 #ifdef ENABLE_NLS
584 lang = language_to_name(current_language);
585 #else
586 lang = "System";
587 #endif
588 lua_pushstring(S, lang);
589 break;
591 case OPT_COLOR:
593 color_T color;
594 unsigned char hexcolor[8];
595 unsigned char *strcolor;
597 color = opt->value.color;
598 strcolor = get_color_string(color, hexcolor);
599 lua_pushstring(S, strcolor);
600 break;
602 case OPT_COMMAND:
603 default:
604 goto lua_error;
607 return 1;
609 lua_error:
610 lua_pushnil(S);
611 return 1;
614 /* End of set/get option */
617 eval_function(LS, int num_args, int num_results)
619 int err;
621 err = lua_pcall(S, num_args, num_results, 0);
622 if (err) {
623 alert_lua_error((unsigned char *) lua_tostring(L, -1));
624 lua_pop(L, 1);
627 return err;
630 /* Initialisation */
632 static void
633 do_hooks_file(LS, unsigned char *prefix, unsigned char *filename)
635 unsigned char *file = straconcat(prefix, "/", filename, NULL);
637 if (!file) return;
639 /* Test file existence to avoid Lua error reporting (under version 5.x)
640 * Fixes debian bug #231760 ('dbug 231760' using URI rewrite) */
641 if (file_can_read(file)) {
642 int oldtop = lua_gettop(S);
644 if (lua_dofile(S, file) != 0)
645 sleep(3); /* Let some time to see error messages. */
646 lua_settop(S, oldtop);
649 mem_free(file);
652 void
653 init_lua(struct module *module)
655 L = lua_open();
657 luaopen_base(L);
658 luaopen_table(L);
659 luaopen_io(L);
660 luaopen_string(L);
661 luaopen_math(L);
663 lua_register(L, LUA_ALERT, l_alert);
664 lua_register(L, "current_url", l_current_url);
665 lua_register(L, "current_link", l_current_link);
666 lua_register(L, "current_title", l_current_title);
667 lua_register(L, "current_document", l_current_document);
668 lua_register(L, "current_document_formatted", l_current_document_formatted);
669 lua_register(L, "pipe_read", l_pipe_read);
670 lua_register(L, "execute", l_execute);
671 lua_register(L, "tmpname", l_tmpname);
672 lua_register(L, "bind_key", l_bind_key);
673 lua_register(L, "edit_bookmark_dialog", l_edit_bookmark_dialog);
674 lua_register(L, "xdialog", l_xdialog);
675 lua_register(L, "set_option", l_set_option);
676 lua_register(L, "get_option", l_get_option);
678 lua_pushstring(L, elinks_home ? elinks_home
679 : (unsigned char *) CONFDIR);
680 lua_setglobal(L, "elinks_home");
682 do_hooks_file(L, CONFDIR, LUA_HOOKS_FILENAME);
683 if (elinks_home) do_hooks_file(L, elinks_home, LUA_HOOKS_FILENAME);
686 static void free_lua_console_history_entries(void);
688 void
689 cleanup_lua(struct module *module)
691 free_lua_console_history_entries();
692 lua_close(L);
695 /* Attempt to handle infinite loops by trapping SIGINT. If we get a
696 * SIGINT, we longjump to where prepare_lua was called. finish_lua()
697 * disables the trapping. */
699 static void
700 handle_sigint(void *data)
702 finish_lua();
703 siglongjmp(errjmp, -1);
707 prepare_lua(struct session *ses)
709 lua_ses = ses;
710 errterm = lua_ses ? lua_ses->tab->term : NULL;
711 /* XXX this uses the wrong term, I think */
712 install_signal_handler(SIGINT, (void (*)(void *)) handle_sigint, NULL, 1);
714 return sigsetjmp(errjmp, 1);
717 void
718 finish_lua(void)
720 /* XXX should save previous handler instead of assuming this one */
721 install_signal_handler(SIGINT, (void (*)(void *)) sig_ctrl_c, errterm, 0);
725 /* Error reporting. */
727 void
728 alert_lua_error(unsigned char *msg)
730 if (errterm) {
731 info_box(errterm, MSGBOX_NO_TEXT_INTL | MSGBOX_FREE_TEXT,
732 N_("Lua Error"), ALIGN_LEFT,
733 stracpy(msg));
734 return;
737 usrerror("Lua: %s", msg);
738 sleep(3);
741 void
742 alert_lua_error2(unsigned char *msg, unsigned char *msg2)
744 unsigned char *tmp = stracpy(msg);
746 if (!tmp) return;
747 add_to_strn(&tmp, msg2);
748 alert_lua_error(tmp);
749 mem_free(tmp);
753 /* The following stuff is to handle the return values of
754 * lua_console_hook and keystroke functions, and also the xdialog
755 * function. It expects two values on top of the stack. */
757 static void
758 handle_ret_eval(struct session *ses)
760 const unsigned char *expr = lua_tostring(L, -1);
762 if (expr) {
763 int oldtop = lua_gettop(L);
765 if (prepare_lua(ses) == 0) {
766 lua_dostring(L, expr);
767 lua_settop(L, oldtop);
768 finish_lua();
770 return;
773 alert_lua_error("bad argument for eval");
776 static void
777 handle_ret_run(struct session *ses)
779 unsigned char *cmd = (unsigned char *) lua_tostring(L, -1);
781 if (cmd) {
782 exec_on_terminal(ses->tab->term, cmd, "", 1);
783 return;
786 alert_lua_error("bad argument for run");
789 static void
790 handle_ret_goto_url(struct session *ses)
792 unsigned char *url = (unsigned char *) lua_tostring(L, -1);
794 if (url) {
795 goto_url_with_hook(ses, url);
796 return;
799 alert_lua_error("bad argument for goto_url");
802 static void
803 handle_standard_lua_returns(unsigned char *from)
805 const unsigned char *act = lua_tostring(L, -2);
807 if (act) {
808 if (!strcmp(act, "eval"))
809 handle_ret_eval(lua_ses);
810 else if (!strcmp(act, "run"))
811 handle_ret_run(lua_ses);
812 else if (!strcmp(act, "goto_url"))
813 handle_ret_goto_url(lua_ses);
814 else
815 alert_lua_error2("unrecognised return value from ", from);
817 else if (!lua_isnil(L, -2))
818 alert_lua_error2("bad return type from ", from);
820 lua_pop(L, 2);
823 static void
824 handle_ref_on_stack(LS, struct session *ses, unsigned char *from, int num_args)
826 int err;
828 if (prepare_lua(ses)) return;
829 err = eval_function(S, num_args, 2);
830 finish_lua();
832 if (!err) handle_standard_lua_returns(from);
835 static void
836 handle_ref(LS, struct session *ses, int func_ref, unsigned char *from,
837 int num_args, int unref)
839 lua_rawgeti(S, LUA_REGISTRYINDEX, func_ref);
841 /* The function must be below the arguments on the stack. */
842 if (num_args != 0) lua_insert(S, -(num_args + 1));
844 handle_ref_on_stack(S, ses, from, num_args);
846 if (unref)
847 luaL_unref(S, LUA_REGISTRYINDEX, func_ref);
851 /* Console stuff. */
853 static INIT_INPUT_HISTORY(lua_console_history);
855 static void
856 lua_console(struct session *ses, unsigned char *expr)
858 lua_getglobal(L, "lua_console_hook");
859 if (lua_isnil(L, -1)) {
860 lua_pop(L, 1);
861 handle_ret_eval(ses);
862 return;
865 lua_pushstring(L, expr);
866 handle_ref_on_stack(L, ses, "lua_console_hook", 1);
869 /* TODO: Make this a "Scripting console" instead, with a radiobutton below the
870 * inputbox selecting the appropriate scripting backend to use to evaluate the
871 * expression. --pasky */
873 enum evhook_status
874 dialog_lua_console(va_list ap, void *data)
876 struct session *ses = va_arg(ap, struct session *);
878 if (get_cmd_opt_bool("anonymous"))
879 return EVENT_HOOK_STATUS_NEXT;
881 input_dialog(ses->tab->term, NULL,
882 N_("Lua Console"), N_("Enter expression"),
883 ses, &lua_console_history,
884 MAX_STR_LEN, "", 0, 0, NULL,
885 (void (*)(void *, unsigned char *)) lua_console, NULL);
886 return EVENT_HOOK_STATUS_NEXT;
889 static void
890 free_lua_console_history_entries(void)
892 free_list(lua_console_history.entries);
895 enum evhook_status
896 free_lua_console_history(va_list ap, void *data)
898 free_lua_console_history_entries();
899 return EVENT_HOOK_STATUS_NEXT;