1
0
mirror of https://github.com/rkd77/elinks.git synced 2024-09-26 02:46:13 -04:00

Add the basics for _browser_ (ecma)scripting ELinks with SEE

SEE is David Leonard's Simple Ecmascript Engine.  The SEE scripting backend
is very raw and not tested very much. The idea was to see what kind of
creature SEE is (and contradict pasky's aired opinion that no new features
are added anymore ;).

echo 'function goto_url() { return 'localhost'; }' > ~/.elinks/hooks.js
and get local for maximum security ...

FYI: SEE is smaller than Spidermonkey but doesn't have the same kind of
data-driven interface, although it looks like it is possible to build that.
This commit is contained in:
Jonas Fonseca 2005-10-20 20:38:01 +02:00 committed by Jonas Fonseca
parent c4c034804b
commit 0ba773e7a5
12 changed files with 616 additions and 1 deletions

View File

@ -74,6 +74,7 @@ PYTHON_LIBS = @PYTHON_LIBS@
RANLIB = @RANLIB@
RUBY_CFLAGS = @RUBY_CFLAGS@
RUBY_LIBS = @RUBY_LIBS@
SEE_CFLAGS = @SEE_CFLAGS@
SPIDERMONKEY_CFLAGS = @SPIDERMONKEY_CFLAGS@
SPIDERMONKEY_LIBS = @SPIDERMONKEY_LIBS@
VERSION = @VERSION@
@ -130,6 +131,7 @@ CONFIG_RISCOS = @CONFIG_RISCOS@
CONFIG_RUBY = @CONFIG_RUBY@
CONFIG_SCANNER = @CONFIG_SCANNER@
CONFIG_SCRIPTING = @CONFIG_SCRIPTING@
CONFIG_SEE = @CONFIG_SEE@
CONFIG_SHA1 = @CONFIG_SHA1@
CONFIG_SMALL = @CONFIG_SMALL@
CONFIG_SMB = @CONFIG_SMB@

48
config/m4/see.m4 Normal file
View File

@ -0,0 +1,48 @@
AC_DEFUN([EL_CONFIG_SEE],
[
enable_see="no";
AC_ARG_WITH(see, [ --with-see enable Simple Ecmascript Engine (SEE) support],
[ if test "x$withval" != xno; then enable_see=yes; fi ])
# The following is probably bad, ugly and so on. Stolen from Guile's (1.4)
# SEE_FLAGS but I really don't want to require people to have Guile in order
# to compile CVS. Also, the macro seems to be really stupid regarding searching
# for Guile in $PATH etc. --pasky
AC_MSG_CHECKING([for SEE])
if test "$enable_see" = "yes"; then
AC_MSG_RESULT(yes);
## Based on the SEE_FLAGS macro.
if test -d "$withval"; then
SEE_PATH="$withval:$PATH"
else
SEE_PATH="$PATH"
fi
AC_PATH_PROG(SEE_CONFIG, libsee-config, no, $SEE_PATH)
## First, let's just see if we can find Guile at all.
if test "$SEE_CONFIG" != no; then
cf_result="yes";
SEE_LIBS="`$SEE_CONFIG --libs`"
SEE_CFLAGS="`$SEE_CONFIG --cppflags`"
LIBS="$SEE_LIBS $LIBS"
CPPFLAGS="$CPPFLAGS $SEE_CFLAGS"
EL_CONFIG(CONFIG_SEE, [SEE])
AC_SUBST(SEE_CFLAGS)
else
if test -n "$withval" && test "x$withval" != xno; then
AC_MSG_ERROR([SEE not found])
else
AC_MSG_WARN([SEE support disabled])
fi
fi
else
AC_MSG_RESULT(no);
fi
])

View File

@ -708,16 +708,23 @@ dnl ===================================================================
EL_CONFIG_RUBY
dnl ===================================================================
dnl Check for SEE, optional even if installed.
dnl ===================================================================
EL_CONFIG_SEE
dnl ===================================================================
dnl Setup global scripting
dnl ===================================================================
EL_CONFIG_DEPENDS(CONFIG_SCRIPTING, [CONFIG_GUILE CONFIG_LUA CONFIG_PERL CONFIG_PYTHON CONFIG_RUBY], [Scripting])
EL_CONFIG_DEPENDS(CONFIG_SCRIPTING, [CONFIG_GUILE CONFIG_LUA CONFIG_PERL CONFIG_PYTHON CONFIG_RUBY CONFIG_SEE], [Scripting])
AC_SUBST(CONFIG_GUILE)
AC_SUBST(CONFIG_LUA)
AC_SUBST(CONFIG_PERL)
AC_SUBST(CONFIG_PYTHON)
AC_SUBST(CONFIG_RUBY)
AC_SUBST(CONFIG_SEE)
AC_SUBST(CONFIG_SCRIPTING)

View File

@ -6,6 +6,7 @@ SUBDIRS-$(CONFIG_LUA) += lua
SUBDIRS-$(CONFIG_PERL) += perl
SUBDIRS-$(CONFIG_PYTHON) += python
SUBDIRS-$(CONFIG_RUBY) += ruby
SUBDIRS-$(CONFIG_SEE) += see
OBJS = scripting.o

View File

@ -19,6 +19,7 @@
#include "scripting/perl/perl.h"
#include "scripting/python/python.h"
#include "scripting/ruby/ruby.h"
#include "scripting/see/see.h"
static struct module *scripting_modules[] = {
@ -36,6 +37,9 @@ static struct module *scripting_modules[] = {
#endif
#ifdef CONFIG_RUBY
&ruby_scripting_module,
#endif
#ifdef CONFIG_SEE
&see_scripting_module,
#endif
NULL,
};

View File

@ -0,0 +1,8 @@
top_builddir=../../..
include $(top_builddir)/Makefile.config
INCLUDES += $(SEE_CFLAGS)
OBJS = see.o hooks.o core.o
include $(top_srcdir)/Makefile.lib

249
src/scripting/see/core.c Normal file
View File

@ -0,0 +1,249 @@
/* Ruby interface (scripting engine) */
/* $Id: core.c,v 1.14 2005/06/14 12:25:21 jonas Exp $ */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <see/see.h>
#include "elinks.h"
#include "bfu/dialog.h"
#include "config/home.h"
#include "intl/gettext/libintl.h"
#include "main/module.h"
#include "scripting/see/core.h"
#include "session/session.h"
#include "terminal/terminal.h"
#include "terminal/window.h"
#include "util/error.h"
#include "util/file.h"
#include "util/string.h"
#define SEE_HOOKS_FILENAME "hooks.js"
struct SEE_interpreter see_interpreter;
struct SEE_object *see_browser_object;
#if 0
/* SEE strings */
static struct SEE_string[] = {
{ 'E', 'L', 'i', 'n', 'k', 's' },
{ "goto-url", 0, script_hook_goto_url, NULL },
{ "follow-url", 0, script_hook_follow_url, NULL },
{ "pre-format-html", 0, script_hook_pre_format_html, NULL },
{ "get-proxy", 0, script_hook_get_proxy, NULL },
{ "quit", 0, script_hook_quit, NULL },
{ 0 },
};
#endif
struct string *
convert_see_string(struct string *string, struct SEE_string *source)
{
unsigned int pos;
if (!init_string(string))
return NULL;
for (pos = 0; pos < source->length; pos++) {
add_char_to_string(string, (unsigned char) source->data[pos]);
}
return string;
}
/* Error reporting. */
void
alert_see_error(struct session *ses, unsigned char *msg)
{
struct terminal *term;
if (!ses) {
if (list_empty(terminals)) {
usrerror("[SEE script] %s", msg);
return;
}
term = terminals.next;
} else {
term = ses->tab->term;
}
msg = stracpy(msg);
if (!msg) return;
info_box(term, MSGBOX_NO_TEXT_INTL | MSGBOX_FREE_TEXT,
N_("SEE error"), ALIGN_LEFT, msg);
}
/* The ELinks module: */
static void
elinks_see_write(struct SEE_interpreter *see, struct SEE_object *self,
struct SEE_object *thisobj, int argc, struct SEE_value **argv,
struct SEE_value *res)
{
struct SEE_value v;
struct string string;
SEE_SET_UNDEFINED(res);
if (!argc) return;
SEE_ToString(see, argv[0], &v);
if (!convert_see_string(&string, v.u.string))
return;
if (list_empty(terminals)) {
usrerror("[SEE] ", string.source);
done_string(&string);
return;
}
info_box(terminals.next, MSGBOX_NO_TEXT_INTL | MSGBOX_FREE_TEXT,
N_("SEE Message"), ALIGN_LEFT, string.source);
}
#if 0
struct object_info browser_object[] = {
"ELinks",
{ /* Properties: */
{ "version", SEE_STRING, VERSION, SEE_READONLY },
{ "home", ... },
{ NULL }
},
{ /* Methods: (as name, handler, args) */
{ "write", elinks_see_write, 1 },
{ NULL }
},
};
#endif
static void
init_see_environment(struct SEE_interpreter *see)
{
unsigned char *home;
struct SEE_object *obj, *elinks;
struct SEE_value value;
struct SEE_string *name;
/* TODO: Initialize strings.
SEE_intern_global(s_print = &S_print);
* */
/* Create the elinks browser object. Add it to the global space */
elinks = SEE_Object_new(see);
SEE_SET_OBJECT(&value, elinks);
name = SEE_string_sprintf(see, "ELinks");
SEE_OBJECT_PUT(see, see->Global, name, &value, 0);
/* Create a string and attach as 'ELinks.version' */
SEE_SET_STRING(&value, SEE_string_sprintf(see, VERSION));
name = SEE_string_sprintf(see, "version");
SEE_OBJECT_PUT(see, elinks, name, &value, SEE_ATTR_READONLY);
/* Create a string and attach as 'ELinks.home' */
home = elinks_home ? elinks_home : (unsigned char *) CONFDIR;
SEE_SET_STRING(&value, SEE_string_sprintf(see, home));
name = SEE_string_sprintf(see, "home");
SEE_OBJECT_PUT(see, elinks, name, &value, SEE_ATTR_READONLY);
/* Create a 'write' method and attach to the browser object. */
name = SEE_string_sprintf(see, "write");
obj = SEE_cfunction_make(see, elinks_see_write, name, 1);
SEE_SET_OBJECT(&value, obj);
SEE_OBJECT_PUT(see, elinks, name, &value, 0);
}
static void
see_abort_handler(struct SEE_interpreter *see, const char *msg)
{
alert_see_error(NULL, (unsigned char *) msg);
}
static void
see_oom_handler(struct SEE_interpreter *see)
{
/* XXX: Ignore! */
}
void
init_see(struct module *module)
{
struct SEE_interpreter *see = &see_interpreter;
unsigned char *path;
FILE *file;
SEE_abort = see_abort_handler;
SEE_mem_exhausted_hook = see_oom_handler;
/* Initialise an interpreter */
SEE_interpreter_init(see);
/* Set up the ELinks module interface. */
init_see_environment(see);
if (elinks_home) {
path = straconcat(elinks_home, SEE_HOOKS_FILENAME, NULL);
} else {
path = stracpy(CONFDIR "/" SEE_HOOKS_FILENAME);
}
if (!path) return;
file = fopen(path, "r");
if (file) {
struct SEE_value result;
struct SEE_input *input;
SEE_try_context_t try_context;
struct SEE_value *exception;
/* Load ~/.elinks/hooks.js into the interpreter. */
input = SEE_input_file(see, file, path, NULL);
SEE_TRY(see, try_context) {
SEE_Global_eval(see, input, &result);
}
SEE_INPUT_CLOSE(input);
exception = SEE_CAUGHT(try_context);
if (exception) {
SEE_try_context_t try_context2;
struct SEE_value value;
fprintf(stderr, "errors encountered while reading %s:", path);
SEE_TRY(see, try_context2) {
SEE_ToString(see, exception, &value);
SEE_string_fputs(value.u.string, stderr);
#if 0
if (ctxt.throw_file)
fprintf(stderr, " (thrown from %s:%d)\n",
ctxt.throw_file, ctxt.throw_line);
#endif
SEE_PrintTraceback(see, stderr);
}
if (SEE_CAUGHT(try_context2)) {
fprintf(stderr, "exception thrown while "
"printing exception");
#if 0
if (ctxt2.throw_file)
fprintf(stderr, " at %s:%d",
ctxt2.throw_file, ctxt2.throw_line);
#endif
fprintf(stderr, "\n");
}
}
}
mem_free(path);
}

21
src/scripting/see/core.h Normal file
View File

@ -0,0 +1,21 @@
/* $Id: core.h,v 1.3 2005/04/01 17:20:59 zas Exp $ */
#ifndef EL__SCRIPTING_SEE_CORE_H
#define EL__SCRIPTING_SEE_CORE_H
#include <see/see.h>
struct module;
struct session;
struct string;
extern struct SEE_interpreter see_interpreter;
extern struct SEE_object *see_browser_object;
struct string *convert_see_string(struct string *string, struct SEE_string *source);
void alert_see_error(struct session *ses, unsigned char *msg);
void see_report_error(struct session *ses, int state);
void init_see(struct module *module);
#endif

232
src/scripting/see/hooks.c Normal file
View File

@ -0,0 +1,232 @@
/* Ruby scripting hooks */
/* $Id: hooks.c,v 1.14 2005/06/14 12:25:21 jonas Exp $ */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <see/see.h>
#include "elinks.h"
#include "main/event.h"
#include "protocol/uri.h"
#include "scripting/see/core.h"
#include "scripting/see/hooks.h"
#include "session/location.h"
#include "session/session.h"
#include "util/string.h"
/* The events that will trigger the functions below and what they are expected
* to do is explained in doc/events.txt */
static struct SEE_value *
call_see_hook(struct SEE_interpreter *see, unsigned char *name,
struct SEE_value *args[], int argc,
struct SEE_value *result)
{
struct SEE_string *hook_name = SEE_string_sprintf(see, name);
struct SEE_value hook;
SEE_OBJECT_GET(see, see->Global, hook_name, &hook);
if (SEE_VALUE_GET_TYPE(&hook) != SEE_OBJECT
|| !SEE_OBJECT_HAS_CALL(hook.u.object))
return NULL;
SEE_OBJECT_CALL(see, hook.u.object, NULL, argc, args, result);
#if 0
if (error) {
erb_report_error(NULL, error);
return EVENT_HOOK_STATUS_NEXT;
}
#endif
return result;
}
static enum evhook_status
script_hook_goto_url(va_list ap, void *data)
{
unsigned char **url = va_arg(ap, unsigned char **);
struct session *ses = va_arg(ap, struct session *);
struct SEE_interpreter *see = &see_interpreter;
struct SEE_value args_[2], *args[2] = { &args_[0], &args_[1] };
struct SEE_value result;
if (*url == NULL)
return EVENT_HOOK_STATUS_NEXT;
SEE_SET_STRING(args[0], SEE_string_sprintf(see, "%s", *url));
if (!ses || !have_location(ses)) {
SEE_SET_UNDEFINED(args[1]);
} else {
SEE_SET_STRING(args[1],
SEE_string_sprintf(see, "%s", struri(cur_loc(ses)->vs.uri)));
}
if (!call_see_hook(see, "goto_url", args, sizeof_array(args), &result))
return EVENT_HOOK_STATUS_NEXT;
switch (SEE_VALUE_GET_TYPE(&result)) {
case SEE_STRING:
{
struct string new_url;
if (convert_see_string(&new_url, result.u.string))
mem_free_set(url, new_url.source);
break;
}
case SEE_NULL:
break;
default:
alert_see_error(ses, "goto_url_hook must return a string or null");
}
return EVENT_HOOK_STATUS_NEXT;
}
static enum evhook_status
script_hook_follow_url(va_list ap, void *data)
{
unsigned char **url = va_arg(ap, unsigned char **);
struct session *ses = va_arg(ap, struct session *);
struct SEE_interpreter *see = &see_interpreter;
struct SEE_value args_[1], *args[1] = { &args_[0] };
struct SEE_value result;
evhook_use_params(url && ses);
if (*url == NULL)
return EVENT_HOOK_STATUS_NEXT;
SEE_SET_STRING(args[0], SEE_string_sprintf(see, "%s", *url));
if (!call_see_hook(see, "follow_url", args, sizeof_array(args), &result))
return EVENT_HOOK_STATUS_NEXT;
switch (SEE_VALUE_GET_TYPE(&result)) {
case SEE_STRING:
{
struct string new_url;
if (convert_see_string(&new_url, result.u.string))
mem_free_set(url, new_url.source);
break;
}
case SEE_NULL:
break;
default:
alert_see_error(ses, "follow_url_hook must return a string or null");
}
return EVENT_HOOK_STATUS_NEXT;
}
static enum evhook_status
script_hook_pre_format_html(va_list ap, void *data)
{
unsigned char **html = va_arg(ap, unsigned char **);
int *html_len = va_arg(ap, int *);
struct session *ses = va_arg(ap, struct session *);
unsigned char *url = va_arg(ap, unsigned char *);
struct SEE_interpreter *see = &see_interpreter;
struct SEE_value args_[2], *args[2] = { &args_[0], &args_[1] };
struct SEE_value result;
evhook_use_params(url && ses);
if (*html == NULL || *html_len == 0)
return EVENT_HOOK_STATUS_NEXT;
SEE_SET_STRING(args[0], SEE_string_sprintf(see, "%s", url));
SEE_SET_STRING(args[1], SEE_string_sprintf(see, "%s", *html));
if (!call_see_hook(see, "pre_format_html", args, sizeof_array(args), &result))
return EVENT_HOOK_STATUS_NEXT;
switch (SEE_VALUE_GET_TYPE(&result)) {
case SEE_STRING:
{
struct string new_html;
if (convert_see_string(&new_html, result.u.string)) {
mem_free_set(html, new_html.source);
*html_len = new_html.length;
}
break;
}
case SEE_NULL:
break;
default:
alert_see_error(ses, "pre_format_hook must return a string or null");
}
return EVENT_HOOK_STATUS_NEXT;
}
/* The function can return:
* - "PROXY:PORT" to use the specified proxy
* - "" to not use any proxy
* - nil to use the default proxies */
static enum evhook_status
script_hook_get_proxy(va_list ap, void *data)
{
unsigned char **new_proxy_url = va_arg(ap, unsigned char **);
unsigned char *url = va_arg(ap, unsigned char *);
struct SEE_interpreter *see = &see_interpreter;
struct SEE_value result, args_[1], *args[1] = { &args_[0] };
if (!new_proxy_url || !url)
return EVENT_HOOK_STATUS_NEXT;
SEE_SET_STRING(args[0], SEE_string_sprintf(see, "%s", url));
if (!call_see_hook(see, "get_proxy", args, sizeof_array(args), &result))
return EVENT_HOOK_STATUS_NEXT;
switch (SEE_VALUE_GET_TYPE(&result)) {
case SEE_STRING:
{
struct string proxy;
if (convert_see_string(&proxy, result.u.string))
mem_free_set(new_proxy_url, proxy.source);
break;
}
case SEE_NULL:
break;
default:
alert_see_error(NULL, "proxy_hook must return a string, undefined or null");
}
return EVENT_HOOK_STATUS_NEXT;
}
static enum evhook_status
script_hook_quit(va_list ap, void *data)
{
struct SEE_interpreter *see = &see_interpreter;
struct SEE_value result;
call_see_hook(see, "quit", NULL, 0, &result);
return EVENT_HOOK_STATUS_NEXT;
}
struct event_hook_info see_scripting_hooks[] = {
{ "goto-url", 0, script_hook_goto_url, NULL },
{ "follow-url", 0, script_hook_follow_url, NULL },
{ "pre-format-html", 0, script_hook_pre_format_html, NULL },
{ "get-proxy", 0, script_hook_get_proxy, NULL },
{ "quit", 0, script_hook_quit, NULL },
NULL_EVENT_HOOK_INFO,
};

10
src/scripting/see/hooks.h Normal file
View File

@ -0,0 +1,10 @@
/* $Id: hooks.h,v 1.2 2005/04/01 17:42:56 zas Exp $ */
#ifndef EL__SCRIPTING_SEE_HOOKS_H
#define EL__SCRIPTING_SEE_HOOKS_H
struct event_hook_info;
extern struct event_hook_info see_scripting_hooks[];
#endif

23
src/scripting/see/see.c Normal file
View File

@ -0,0 +1,23 @@
/* Simple Ecmascript Engine (SEE) browser scripting module */
/* $Id: see.c,v 1.2 2005/06/13 00:43:29 jonas Exp $ */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "elinks.h"
#include "main/module.h"
#include "scripting/see/core.h"
#include "scripting/see/hooks.h"
struct module see_scripting_module = struct_module(
/* name: */ "Simple Ecmascript Engine",
/* options: */ NULL,
/* events: */ see_scripting_hooks,
/* submodules: */ NULL,
/* data: */ NULL,
/* init: */ init_see,
/* done: */ NULL
);

10
src/scripting/see/see.h Normal file
View File

@ -0,0 +1,10 @@
/* $Id: see.h,v 1.4 2005/04/06 22:33:42 jonas Exp $ */
#ifndef EL__SCRIPTING_SEE_SEE_H
#define EL__SCRIPTING_SEE_SEE_H
struct module;
extern struct module see_scripting_module;
#endif