1 /* Lua interface (scripting engine) */
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"
54 static struct session
*lua_ses
;
55 static struct terminal
*errterm
;
56 static sigjmp_buf errjmp
;
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.
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). */
77 msg
= "(cannot convert the error message to a string)";
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
);
91 lua_pushstring(S
, url
);
104 struct link
*link
= get_current_session_link(lua_ses
);
107 lua_pushstring(S
, link
->where
);
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
);
124 sanitize_title(clean_title
);
126 lua_pushstring(S
, clean_title
);
127 mem_free(clean_title
);
137 l_current_document(LS
)
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
);
153 /* XXX: This function is mostly copied from `dump_to_file'. */
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
;
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
);
179 lua_ses
->tab
->term
->width
= old_width
;
180 render_document_frames(lua_ses
, 0);
193 unsigned char *s
= NULL
;
196 if (!lua_isstring(S
, 1)) goto lua_error
;
198 fp
= popen(lua_tostring(S
, 1), "r");
199 if (!fp
) goto lua_error
;
202 unsigned char buf
[1024];
203 size_t l
= fread(buf
, 1, sizeof(buf
), fp
);
206 unsigned char *news
= mem_realloc(s
, len
+ l
);
208 if (!news
) goto lua_error
;
210 memcpy(s
+ len
, buf
, l
);
219 lua_pushlstring(S
, s
, len
);
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);
245 unsigned char *fn
= tempnam(NULL
, "elinks");
248 lua_pushstring(S
, fn
);
253 alert_lua_error("Error generating temporary file name");
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
;
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");
292 if (!init_string(&event_name
)) goto lua_error
;
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
);
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");
312 luaL_unref(S
, LUA_REGISTRYINDEX
, ref
);
313 alert_lua_error2("error in bind_key: ", err
);
317 lua_pushnumber(S
, 1);
326 /* Begin very hackish bit for bookmark editing dialog. */
327 /* XXX: Add history and generalise. */
329 struct lua_dlg_data
{
331 unsigned char cat
[MAX_STR_LEN
];
332 unsigned char name
[MAX_STR_LEN
];
333 unsigned char url
[MAX_STR_LEN
];
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);
350 l_edit_bookmark_dialog(LS
)
352 struct terminal
*term
= lua_ses
->tab
->term
;
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)) {
362 #define L_EDIT_BMK_WIDGETS_COUNT 5
363 dlg
= calloc_dialog(L_EDIT_BMK_WIDGETS_COUNT
, sizeof(*data
));
366 data
= (struct lua_dlg_data
*) get_dialog_offset(dlg
, L_EDIT_BMK_WIDGETS_COUNT
);
368 safe_strncpy(data
->cat
, (unsigned char *) lua_tostring(S
, 1),
370 safe_strncpy(data
->name
, (unsigned char *) lua_tostring(S
, 2),
372 safe_strncpy(data
->url
, (unsigned char *) lua_tostring(S
, 3),
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);
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
{
408 unsigned char fields
[XDIALOG_MAX_FIELDS
][MAX_STR_LEN
];
412 xdialog_run_lua(void *data_
)
414 struct lua_xdialog_data
*data
= data_
;
415 lua_State
*s
= data
->state
;
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",
426 struct terminal
*term
;
428 struct lua_xdialog_data
*data
;
429 int nargs
, nfields
, nitems
;
432 if (!lua_ses
) return 0;
434 term
= lua_ses
->tab
->term
;
436 nargs
= lua_gettop(S
);
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
));
447 data
= (struct lua_xdialog_data
*) get_dialog_offset(dlg
, nitems
);
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),
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);
480 /* End xdialog bit. */
488 struct option
*opt
, *current
;
491 nargs
= lua_gettop(S
);
495 /* Get option record */
496 name
= lua_tostring(S
, 1);
497 opt
= get_opt_rec(config_options
, (unsigned char *) name
);
507 value
= lua_toboolean(S
, 2);
508 option_types
[opt
->type
].set(opt
, (unsigned char *) (&value
));
517 value
= lua_tonumber(S
, 2);
518 option_types
[opt
->type
].set(opt
, (unsigned char *) (&value
));
525 option_types
[opt
->type
].set(opt
, (unsigned char *) lua_tostring(S
, 2));
531 opt
->flags
|= OPT_TOUCHED
;
535 call_change_hooks(lua_ses
, current
, opt
);
550 /* Get option record */
551 nargs
= lua_gettop(S
);
554 name
= lua_tostring(S
, 1);
555 opt
= get_opt_rec(config_options
, (unsigned char *) name
);
559 /* Convert to an appropriate Lua type */
562 lua_pushboolean(S
, opt
->value
.number
);
566 lua_pushnumber(S
, opt
->value
.number
);
569 lua_pushstring(S
, opt
->value
.string
);
573 unsigned char *cp_name
;
575 cp_name
= get_cp_mime_name(opt
->value
.number
);
576 lua_pushstring(S
, cp_name
);
584 lang
= language_to_name(current_language
);
588 lua_pushstring(S
, lang
);
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
);
614 /* End of set/get option */
617 eval_function(LS
, int num_args
, int num_results
)
621 err
= lua_pcall(S
, num_args
, num_results
, 0);
623 alert_lua_error((unsigned char *) lua_tostring(L
, -1));
633 do_hooks_file(LS
, unsigned char *prefix
, unsigned char *filename
)
635 unsigned char *file
= straconcat(prefix
, "/", filename
, NULL
);
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
);
653 init_lua(struct module
*module
)
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);
689 cleanup_lua(struct module
*module
)
691 free_lua_console_history_entries();
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. */
700 handle_sigint(void *data
)
703 siglongjmp(errjmp
, -1);
707 prepare_lua(struct session
*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);
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. */
728 alert_lua_error(unsigned char *msg
)
731 info_box(errterm
, MSGBOX_NO_TEXT_INTL
| MSGBOX_FREE_TEXT
,
732 N_("Lua Error"), ALIGN_LEFT
,
737 usrerror("Lua: %s", msg
);
742 alert_lua_error2(unsigned char *msg
, unsigned char *msg2
)
744 unsigned char *tmp
= stracpy(msg
);
747 add_to_strn(&tmp
, msg2
);
748 alert_lua_error(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. */
758 handle_ret_eval(struct session
*ses
)
760 const unsigned char *expr
= lua_tostring(L
, -1);
763 int oldtop
= lua_gettop(L
);
765 if (prepare_lua(ses
) == 0) {
766 lua_dostring(L
, expr
);
767 lua_settop(L
, oldtop
);
773 alert_lua_error("bad argument for eval");
777 handle_ret_run(struct session
*ses
)
779 unsigned char *cmd
= (unsigned char *) lua_tostring(L
, -1);
782 exec_on_terminal(ses
->tab
->term
, cmd
, "", 1);
786 alert_lua_error("bad argument for run");
790 handle_ret_goto_url(struct session
*ses
)
792 unsigned char *url
= (unsigned char *) lua_tostring(L
, -1);
795 goto_url_with_hook(ses
, url
);
799 alert_lua_error("bad argument for goto_url");
803 handle_standard_lua_returns(unsigned char *from
)
805 const unsigned char *act
= lua_tostring(L
, -2);
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
);
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
);
824 handle_ref_on_stack(LS
, struct session
*ses
, unsigned char *from
, int num_args
)
828 if (prepare_lua(ses
)) return;
829 err
= eval_function(S
, num_args
, 2);
832 if (!err
) handle_standard_lua_returns(from
);
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
);
847 luaL_unref(S
, LUA_REGISTRYINDEX
, func_ref
);
853 static INIT_INPUT_HISTORY(lua_console_history
);
856 lua_console(struct session
*ses
, unsigned char *expr
)
858 lua_getglobal(L
, "lua_console_hook");
859 if (lua_isnil(L
, -1)) {
861 handle_ret_eval(ses
);
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 */
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
;
890 free_lua_console_history_entries(void)
892 free_list(lua_console_history
.entries
);
896 free_lua_console_history(va_list ap
, void *data
)
898 free_lua_console_history_entries();
899 return EVENT_HOOK_STATUS_NEXT
;