mirror of
https://github.com/rkd77/elinks.git
synced 2024-10-23 07:20:10 -04:00
Retire the SEE-based *.js scripting backend
This commit is contained in:
parent
9fb9e8700d
commit
9bc71ec8a9
@ -1,48 +0,0 @@
|
|||||||
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
|
|
||||||
])
|
|
@ -724,12 +724,6 @@ dnl ===================================================================
|
|||||||
|
|
||||||
EL_CONFIG_RUBY
|
EL_CONFIG_RUBY
|
||||||
|
|
||||||
dnl ===================================================================
|
|
||||||
dnl Check for SEE, optional even if installed.
|
|
||||||
dnl ===================================================================
|
|
||||||
|
|
||||||
EL_CONFIG_SEE
|
|
||||||
|
|
||||||
dnl ===================================================================
|
dnl ===================================================================
|
||||||
dnl Setup global scripting
|
dnl Setup global scripting
|
||||||
dnl ===================================================================
|
dnl ===================================================================
|
||||||
|
@ -1,220 +0,0 @@
|
|||||||
/* Hooks for the ELinks SEE browser scripting
|
|
||||||
*
|
|
||||||
* Copyright (c) Jonas Fonseca, 2005
|
|
||||||
*/
|
|
||||||
|
|
||||||
function quit()
|
|
||||||
{
|
|
||||||
// alert("quiting ... " + navigator.appVersion);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*********************************************************************
|
|
||||||
* goto_url(url, current_url)
|
|
||||||
*********************************************************************/
|
|
||||||
|
|
||||||
var goto_url_hooks = []
|
|
||||||
|
|
||||||
function goto_url(url, current_url)
|
|
||||||
{
|
|
||||||
var context = {
|
|
||||||
url: url,
|
|
||||||
current_url: current_url ? current_url : ""
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var i = 0; i < goto_url_hooks.length; i++)
|
|
||||||
if (goto_url_hooks[i](context, current_url))
|
|
||||||
break
|
|
||||||
|
|
||||||
return context.url
|
|
||||||
}
|
|
||||||
|
|
||||||
// Don't take localhost as directory name
|
|
||||||
function expand_localhost(context)
|
|
||||||
{
|
|
||||||
if (context.url.match(/localhost/)) {
|
|
||||||
context.url = "http://" + context.url + "/"
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
goto_url_hooks.push(expand_localhost)
|
|
||||||
|
|
||||||
// You can write smt like "gg" to goto URL dialog and it'll go to google.com.
|
|
||||||
// Note that this is obsoleted by the URI rewrite plugin.
|
|
||||||
|
|
||||||
var dumbprefixes = {
|
|
||||||
arc: "http://web.archive.org/web/*/%c",
|
|
||||||
b: "http://babelfish.altavista.com/babelfish/tr",
|
|
||||||
bz: "http://bugzilla.elinks.or.cz",
|
|
||||||
bug: "http://bugzilla.elinks.or.cz",
|
|
||||||
d: "http://www.dict.org",
|
|
||||||
g: "http://www.google.com/",
|
|
||||||
gg: "http://www.google.com/",
|
|
||||||
go: "http://www.google.com/",
|
|
||||||
fm: "http://www.freshmeat.net/",
|
|
||||||
sf: "http://www.sourceforge.net/",
|
|
||||||
dbug: "http://bugs.debian.org/",
|
|
||||||
dpkg: "http://packages.debian.org/",
|
|
||||||
pycur: "http://www.python.org/doc/current/",
|
|
||||||
pydev: "http://www.python.org/dev/doc/devel/",
|
|
||||||
pyhelp: "http://starship.python.net/crew/theller/pyhelp.cgi",
|
|
||||||
pyvault: "http://www.vex.net/parnassus/",
|
|
||||||
e2: "http://www.everything2.org/",
|
|
||||||
sd: "http://www.slashdot.org/",
|
|
||||||
vhtml: "http://validator.w3.org/check?uri=%c",
|
|
||||||
vcss: "http://jigsaw.w3.org/css-validator/validator?uri=%c"
|
|
||||||
}
|
|
||||||
|
|
||||||
function expand_dumbprefix(context, current_url)
|
|
||||||
{
|
|
||||||
if (dumbprefixes[context.url]) {
|
|
||||||
context.url = dumbprefixes[context.url].replace(/%c/, current_url)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
goto_url_hooks.push(expand_dumbprefix)
|
|
||||||
|
|
||||||
function gmane(url)
|
|
||||||
{
|
|
||||||
var match = url.match(/([^\s]+)\s+(.*)$/)
|
|
||||||
var group = match[1]
|
|
||||||
var words = match[2]
|
|
||||||
|
|
||||||
if (!words) return null
|
|
||||||
|
|
||||||
return "http://search.gmane.org/search.php?query=" + words + "&group=" + group
|
|
||||||
}
|
|
||||||
|
|
||||||
function gitweb(base_url)
|
|
||||||
{
|
|
||||||
return function (arguments) {
|
|
||||||
var url = base_url
|
|
||||||
var match = arguments.match(/^(search|summary|shortlog|log|commit|commitdiff|tree)(\s(.*))?/)
|
|
||||||
|
|
||||||
if (match[1])
|
|
||||||
url += ';a=' + match[1]
|
|
||||||
else
|
|
||||||
url += ';a=summary'
|
|
||||||
|
|
||||||
if (match[1] == 'search' && match[3])
|
|
||||||
url += ';s=' + escape(match[3])
|
|
||||||
|
|
||||||
return url
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function bugzilla (base_url)
|
|
||||||
{
|
|
||||||
return function (arguments) {
|
|
||||||
if (!arguments || arguments == '')
|
|
||||||
return base_url
|
|
||||||
|
|
||||||
if (arguments.match(/^[\d]+$/))
|
|
||||||
return base_url + 'show_bug.cgi?id=' + arguments
|
|
||||||
|
|
||||||
return base_url + 'buglist.cgi?short_desc_type=allwordssubstr'
|
|
||||||
+ '&short_desc=' + escape(arguments)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
var smartprefixes = {
|
|
||||||
arc: "http://web.archive.org/web/*/%s",
|
|
||||||
bug: bugzilla('http://bugzilla.elinks.or.cz/'),
|
|
||||||
cambridge: "http://dictionary.cambridge.org/results.asp?searchword=%s",
|
|
||||||
cliki: "http://www.cliki.net/admin/search?words: %s",
|
|
||||||
d: "http://www.dict.org/bin/Dict?Query: %s&Form=Dict1&Strategy=*&Database=*&submit=Submit+query",
|
|
||||||
dmoz: "http://search.dmoz.org/cgi-bin/search?search=%s",
|
|
||||||
foldoc: "http://wombat.doc.ic.ac.uk/foldoc/foldoc.cgi?%s",
|
|
||||||
g: "http://www.google.com/search?q=%s&btnG=Google+Search",
|
|
||||||
gd: "http://www.google.com/search?q=%s&cat=gwd/Top",
|
|
||||||
gg: "http://www.google.com/search?q=%s&btnG=Google+Search",
|
|
||||||
// Whose idea was it to use 'gg' for websearches? -- Miciah
|
|
||||||
//gg = "http://groups.google.com/groups?q=%s",
|
|
||||||
gi: "http://images.google.com/images?q=%s",
|
|
||||||
gitweb: gitweb("http://pasky.or.cz/gitweb.cgi?p=elinks.git"),
|
|
||||||
gmane: gmane,
|
|
||||||
gn: "http://news.google.com/news?q=%s",
|
|
||||||
go: "http://www.google.com/search?q=%s&btnG=Google+Search",
|
|
||||||
gwho: "http://www.googlism.com/?ism=%s&name=1",
|
|
||||||
gwhat: "http://www.googlism.com/?ism=%s&name=2",
|
|
||||||
gwhere: "http://www.googlism.com/?ism=%s&name=3",
|
|
||||||
gwhen: "http://www.googlism.com/?ism=%s&name=4",
|
|
||||||
fm: "http://www.freshmeat.net/search/?q=%s",
|
|
||||||
savannah: "http://savannah.nongnu.org/search/?words=%s&type_of_search=soft&exact=1",
|
|
||||||
sf: "http://sourceforge.net/search/?q=%s",
|
|
||||||
sfp: "http://sourceforge.net/projects/%s",
|
|
||||||
sd: "http://www.slashdot.org/search.pl?query=%s",
|
|
||||||
sdc: "http://www.slashdot.org/search.pl?query=%s&op=comments",
|
|
||||||
sdu: "http://www.slashdot.org/search.pl?query=%s&op=users",
|
|
||||||
sdp: "http://www.slashdot.org/search.pl?query=%s&op=polls",
|
|
||||||
sdj: "http://www.slashdot.org/search.pl?query=%s&op=journals",
|
|
||||||
dbug: "http://bugs.debian.org/%s",
|
|
||||||
dpkg: "http://packages.debian.org/%s",
|
|
||||||
emacs: "http://www.emacswiki.org/cgi-bin/wiki.pl?search=%s",
|
|
||||||
lyrics: "http://music.lycos.com/lyrics/results.asp?QT=L&QW=%s",
|
|
||||||
lxr: "http://lxr.linux.no/ident?i=%s",
|
|
||||||
leo: "http://dict.leo.org/?search=%s",
|
|
||||||
onelook: "http://onelook.com/?w=%s&ls=a",
|
|
||||||
py: "http://starship.python.net/crew/theller/pyhelp.cgi?keyword=%s&version=current",
|
|
||||||
pydev: "http://starship.python.net/crew/theller/pyhelp.cgi?keyword=%s&version=devel",
|
|
||||||
pyvault: "http://py.vaults.ca/apyllo.py?find=%s",
|
|
||||||
e2: "http://www.everything2.org/?node=%s",
|
|
||||||
encz: "http://www.slovnik.cz/bin/ecd?ecd_il=1&ecd_vcb=%s&ecd_trn=translate&ecd_trn_dir=0&ecd_lines=15&ecd_hptxt=0",
|
|
||||||
czen: "http://www.slovnik.cz/bin/ecd?ecd_il=1&ecd_vcb=%s&ecd_trn=translate&ecd_trn_dir=1&ecd_lines=15&ecd_hptxt=0",
|
|
||||||
dict: "http://dictionary.reference.com/search?q=%s",
|
|
||||||
thes: "http://thesaurus.reference.com/search?q=%s",
|
|
||||||
a: "http://acronymfinder.com/af-query.asp?String=exact&Acronym=%s",
|
|
||||||
imdb: "http://imdb.com/Find?%s",
|
|
||||||
mw: "http://www.m-w.com/cgi-bin/dictionary?book=Dictionary&va=%s",
|
|
||||||
mwt: "http://www.m-w.com/cgi-bin/thesaurus?book=Thesaurus&va=%s",
|
|
||||||
whatis: "http://uptime.netcraft.com/up/graph/?host=%s",
|
|
||||||
wiki: "http://www.wikipedia.org/w/wiki.phtml?search=%s",
|
|
||||||
wn: "http://www.cogsci.princeton.edu/cgi-bin/webwn1.7.1?stage=1&word=%s",
|
|
||||||
// rfc by number
|
|
||||||
rfc: "http://www.rfc-editor.org/rfc/rfc%s.txt",
|
|
||||||
// rfc search
|
|
||||||
rfcs: "http://www.rfc-editor.org/cgi-bin/rfcsearch.pl?searchwords=%s&format=http&abstract=abson&keywords=keyon&num=25",
|
|
||||||
cr: "http://www.rfc-editor.org/cgi-bin/rfcsearch.pl?searchwords=%s&format=http&abstract=abson&keywords=keyon&num=25",
|
|
||||||
// Internet Draft search
|
|
||||||
rfcid: "http://www.rfc-editor.org/cgi-bin/idsearch.pl?searchwords=%s&format=http&abstract=abson&keywords=keyon&num=25",
|
|
||||||
urbandict: "http://www.urbandictionary.com/define.php?term=%s",
|
|
||||||
id: "http://www.rfc-editor.org/cgi-bin/idsearch.pl?searchwords=%s&format=http&abstract=abson&keywords=keyon&num=25",
|
|
||||||
draft: "http://www.rfc-editor.org/cgi-bin/idsearch.pl?searchwords=%s&format=http&abstract=abson&keywords=keyon&num=25"
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function expand_smartprefix(context, current_url)
|
|
||||||
{
|
|
||||||
var match = context.url.match(/^([^:\s]+)(:|\s)\s*(.*)\s*$/)
|
|
||||||
|
|
||||||
if (match && match[1] && match[3]) {
|
|
||||||
var nick = match[1]
|
|
||||||
var val = match[3]
|
|
||||||
|
|
||||||
if (smartprefixes[nick]) {
|
|
||||||
if (typeof smartprefixes[nick] == 'string') {
|
|
||||||
context.url = smartprefixes[nick].replace(/%s/, escape(val))
|
|
||||||
return true
|
|
||||||
|
|
||||||
} else if (typeof smartprefixes[nick] == 'function') {
|
|
||||||
context.url = smartprefixes[nick](val)
|
|
||||||
return true
|
|
||||||
|
|
||||||
} else {
|
|
||||||
alert('smartprefix "' + nick + '" has unsupported type "' + typeof smartprefixes[nick] + '".')
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unmatched.
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
goto_url_hooks.push(expand_smartprefix)
|
|
@ -21,7 +21,6 @@
|
|||||||
#include "scripting/perl/perl.h"
|
#include "scripting/perl/perl.h"
|
||||||
#include "scripting/python/python.h"
|
#include "scripting/python/python.h"
|
||||||
#include "scripting/ruby/ruby.h"
|
#include "scripting/ruby/ruby.h"
|
||||||
#include "scripting/see/see.h"
|
|
||||||
#include "scripting/smjs/smjs.h"
|
#include "scripting/smjs/smjs.h"
|
||||||
|
|
||||||
|
|
||||||
@ -78,9 +77,6 @@ static struct module *scripting_modules[] = {
|
|||||||
#ifdef CONFIG_RUBY
|
#ifdef CONFIG_RUBY
|
||||||
&ruby_scripting_module,
|
&ruby_scripting_module,
|
||||||
#endif
|
#endif
|
||||||
#ifdef CONFIG_SEE
|
|
||||||
&see_scripting_module,
|
|
||||||
#endif
|
|
||||||
#ifdef CONFIG_ECMASCRIPT
|
#ifdef CONFIG_ECMASCRIPT
|
||||||
&smjs_scripting_module,
|
&smjs_scripting_module,
|
||||||
#endif
|
#endif
|
||||||
|
@ -1,8 +0,0 @@
|
|||||||
top_builddir=../../..
|
|
||||||
include $(top_builddir)/Makefile.config
|
|
||||||
|
|
||||||
INCLUDES += $(SEE_CFLAGS)
|
|
||||||
|
|
||||||
OBJS = see.o core.o hooks.o interface.o
|
|
||||||
|
|
||||||
include $(top_srcdir)/Makefile.lib
|
|
@ -1,139 +0,0 @@
|
|||||||
/* SEE interface (scripting engine) */
|
|
||||||
|
|
||||||
#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/scripting.h"
|
|
||||||
#include "scripting/see/core.h"
|
|
||||||
#include "scripting/see/interface.h"
|
|
||||||
#include "scripting/see/see.h"
|
|
||||||
#include "util/error.h"
|
|
||||||
#include "util/string.h"
|
|
||||||
|
|
||||||
|
|
||||||
#define SEE_HOOKS_FILENAME "hooks.js"
|
|
||||||
|
|
||||||
struct SEE_interpreter see_interpreter;
|
|
||||||
struct session *see_ses;
|
|
||||||
|
|
||||||
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
report_scripting_error(&see_scripting_module, ses, msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static void
|
|
||||||
see_abort_handler(struct SEE_interpreter *see, const char *msg)
|
|
||||||
{
|
|
||||||
ERROR((unsigned char *) msg);
|
|
||||||
/* TODO: reload scripts! */
|
|
||||||
}
|
|
||||||
|
|
||||||
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 interface. */
|
|
||||||
init_see_interface(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;
|
|
||||||
struct string error_msg;
|
|
||||||
|
|
||||||
/* 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 && init_string(&error_msg)) {
|
|
||||||
SEE_try_context_t try_context2;
|
|
||||||
struct SEE_value value;
|
|
||||||
|
|
||||||
SEE_TRY(see, try_context2) {
|
|
||||||
SEE_ToString(see, exception, &value);
|
|
||||||
convert_see_string(&error_msg, value.u.string);
|
|
||||||
#if 0
|
|
||||||
if (ctxt.throw_file)
|
|
||||||
fprintf(stderr, " (thrown from %s:%d)\n",
|
|
||||||
ctxt.throw_file, ctxt.throw_line);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
WDBG("errors encountered while reading %s:\n%s", path, error_msg.source);
|
|
||||||
done_string(&error_msg);
|
|
||||||
|
|
||||||
if (SEE_CAUGHT(try_context2)) {
|
|
||||||
WDBG("exception thrown while "
|
|
||||||
"printing exception");
|
|
||||||
#if 0
|
|
||||||
if (ctxt2.throw_file)
|
|
||||||
fprintf(stderr, " at %s:%d",
|
|
||||||
ctxt2.throw_file, ctxt2.throw_line);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mem_free(path);
|
|
||||||
}
|
|
@ -1,20 +0,0 @@
|
|||||||
|
|
||||||
#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 session *see_ses;
|
|
||||||
|
|
||||||
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
|
|
@ -1,259 +0,0 @@
|
|||||||
/* Ruby scripting hooks */
|
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
|
||||||
#include "config.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <see/see.h>
|
|
||||||
|
|
||||||
#include "elinks.h"
|
|
||||||
|
|
||||||
#include "cache/cache.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, struct session *ses,
|
|
||||||
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_try_context_t try_context;
|
|
||||||
struct SEE_value *exception;
|
|
||||||
|
|
||||||
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_ses = ses;
|
|
||||||
|
|
||||||
SEE_TRY(see, try_context) {
|
|
||||||
SEE_OBJECT_CALL(see, hook.u.object, NULL, argc, args, result);
|
|
||||||
}
|
|
||||||
|
|
||||||
exception = SEE_CAUGHT(try_context);
|
|
||||||
if (exception) {
|
|
||||||
SEE_try_context_t try_context2;
|
|
||||||
struct SEE_value value;
|
|
||||||
|
|
||||||
SEE_TRY(see, try_context2) {
|
|
||||||
struct string error_msg;
|
|
||||||
|
|
||||||
SEE_ToString(see, exception, &value);
|
|
||||||
|
|
||||||
if (init_string(&error_msg)) {
|
|
||||||
convert_see_string(&error_msg, value.u.string);
|
|
||||||
alert_see_error(ses, error_msg.source);
|
|
||||||
done_string(&error_msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (SEE_CAUGHT(try_context2)) {
|
|
||||||
alert_see_error(ses, "exception thrown while printing exception");
|
|
||||||
}
|
|
||||||
|
|
||||||
see_ses = NULL;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
see_ses = NULL;
|
|
||||||
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_ses = ses;
|
|
||||||
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, ses, "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;
|
|
||||||
|
|
||||||
if (*url == NULL)
|
|
||||||
return EVENT_HOOK_STATUS_NEXT;
|
|
||||||
|
|
||||||
SEE_SET_STRING(args[0], SEE_string_sprintf(see, "%s", *url));
|
|
||||||
|
|
||||||
if (!call_see_hook(see, ses, "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)
|
|
||||||
{
|
|
||||||
struct session *ses = va_arg(ap, struct session *);
|
|
||||||
struct cache_entry *cached = va_arg(ap, struct cache_entry *);
|
|
||||||
struct fragment *fragment = get_cache_fragment(cached);
|
|
||||||
unsigned char *url = struri(cached->uri);
|
|
||||||
struct SEE_interpreter *see = &see_interpreter;
|
|
||||||
struct SEE_value args_[2], *args[2] = { &args_[0], &args_[1] };
|
|
||||||
struct SEE_value result;
|
|
||||||
|
|
||||||
evhook_use_params(ses && cached);
|
|
||||||
|
|
||||||
if (!cached->length || !*fragment->data)
|
|
||||||
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", fragment->length,
|
|
||||||
fragment->data));
|
|
||||||
|
|
||||||
if (!call_see_hook(see, ses, "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)) {
|
|
||||||
add_fragment(cached, 0, new_html.source, new_html.length);
|
|
||||||
normalize_cache_entry(cached, 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, NULL, "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, NULL, "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,
|
|
||||||
};
|
|
@ -1,9 +0,0 @@
|
|||||||
|
|
||||||
#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
|
|
@ -1,209 +0,0 @@
|
|||||||
/* The ELinks SEE interface: */
|
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
|
||||||
#include "config.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <see/see.h>
|
|
||||||
|
|
||||||
#include "elinks.h"
|
|
||||||
|
|
||||||
#include "bfu/dialog.h"
|
|
||||||
#include "config/conf.h"
|
|
||||||
#include "config/home.h"
|
|
||||||
#include "config/options.h"
|
|
||||||
#include "config/opttypes.h"
|
|
||||||
#include "intl/gettext/libintl.h"
|
|
||||||
#include "main/module.h"
|
|
||||||
#include "scripting/scripting.h"
|
|
||||||
#include "scripting/see/core.h"
|
|
||||||
#include "scripting/see/see.h"
|
|
||||||
#include "session/session.h"
|
|
||||||
#include "util/error.h"
|
|
||||||
#include "util/file.h"
|
|
||||||
#include "util/string.h"
|
|
||||||
|
|
||||||
|
|
||||||
static void
|
|
||||||
navigator_preference(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 opt_name;
|
|
||||||
struct option *opt;
|
|
||||||
|
|
||||||
SEE_SET_UNDEFINED(res);
|
|
||||||
|
|
||||||
if (argc != 1 && argc != 2) return;
|
|
||||||
|
|
||||||
SEE_ToString(see, argv[0], &v);
|
|
||||||
if (!convert_see_string(&opt_name, v.u.string))
|
|
||||||
return;
|
|
||||||
|
|
||||||
opt = get_opt_rec(config_options, opt_name.source);
|
|
||||||
done_string(&opt_name);
|
|
||||||
/* FIXME: Alert? */
|
|
||||||
if (!opt) return;
|
|
||||||
|
|
||||||
/* Set option */
|
|
||||||
switch (opt->type) {
|
|
||||||
case OPT_BOOL:
|
|
||||||
{
|
|
||||||
long value = opt->value.number;
|
|
||||||
|
|
||||||
if (argc == 1) {
|
|
||||||
SEE_SET_BOOLEAN(res, value);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
SEE_ToBoolean(see, argv[1], &v);
|
|
||||||
value = !!v.u.boolean;
|
|
||||||
opt->value.number = value;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case OPT_INT:
|
|
||||||
case OPT_LONG:
|
|
||||||
{
|
|
||||||
long value;
|
|
||||||
|
|
||||||
if (argc == 1) {
|
|
||||||
SEE_SET_NUMBER(res, opt->value.number);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
SEE_ToInteger(see, argv[1], &v);
|
|
||||||
value = SEE_ToInt32(see, &v);
|
|
||||||
if (opt->min <= value && value <= opt->max)
|
|
||||||
option_types[opt->type].set(opt, (unsigned char *) (&value));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case OPT_STRING:
|
|
||||||
case OPT_CODEPAGE:
|
|
||||||
case OPT_LANGUAGE:
|
|
||||||
case OPT_COLOR:
|
|
||||||
{
|
|
||||||
struct string opt_value;
|
|
||||||
|
|
||||||
if (argc == 1) {
|
|
||||||
SEE_SET_STRING(res, SEE_string_sprintf(see, opt->value.string));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
SEE_ToString(see, argv[1], &v);
|
|
||||||
if (!convert_see_string(&opt_value, v.u.string))
|
|
||||||
return;
|
|
||||||
|
|
||||||
option_types[opt->type].set(opt, opt_value.source);
|
|
||||||
done_string(&opt_value);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (argc == 2) {
|
|
||||||
opt->flags |= OPT_TOUCHED;
|
|
||||||
call_change_hooks(see_ses, opt, opt);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
navigator_save_preferences(struct SEE_interpreter *see, struct SEE_object *self,
|
|
||||||
struct SEE_object *thisobj, int argc, struct SEE_value **argv,
|
|
||||||
struct SEE_value *res)
|
|
||||||
{
|
|
||||||
if (see_ses)
|
|
||||||
write_config(see_ses->tab->term);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
navigator_alert(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;
|
|
||||||
struct terminal *term;
|
|
||||||
|
|
||||||
SEE_SET_UNDEFINED(res);
|
|
||||||
|
|
||||||
if (!argc) return;
|
|
||||||
|
|
||||||
SEE_ToString(see, argv[0], &v);
|
|
||||||
if (!convert_see_string(&string, v.u.string))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!see_ses && list_empty(terminals)) {
|
|
||||||
usrerror("[SEE] %s", string.source);
|
|
||||||
done_string(&string);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
term = see_ses ? see_ses->tab->term : terminals.next;
|
|
||||||
|
|
||||||
info_box(term, MSGBOX_NO_TEXT_INTL | MSGBOX_FREE_TEXT,
|
|
||||||
N_("SEE Message"), ALIGN_LEFT, string.source);
|
|
||||||
}
|
|
||||||
|
|
||||||
#if DATADRIVEN
|
|
||||||
_IDEA
|
|
||||||
struct object_info browser_object[] = {
|
|
||||||
"ELinks", SEE_ATTR_READONLY,
|
|
||||||
{ /* Properties: */
|
|
||||||
{ "version", SEE_STRING, VERSION, SEE_ATTR_READONLY },
|
|
||||||
{ "home", SEE_STRING, NULL, SEE_ATTR_READONLY },
|
|
||||||
},
|
|
||||||
{ /* Methods: (as name, handler, args) */
|
|
||||||
{ "write", elinks_see_write, SEE_ATTR_READONLY },
|
|
||||||
{ NULL }
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
struct object_info *see_
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void
|
|
||||||
init_see_interface(struct SEE_interpreter *see)
|
|
||||||
{
|
|
||||||
unsigned char *home;
|
|
||||||
struct SEE_object *obj, *navigator;
|
|
||||||
struct SEE_value value;
|
|
||||||
struct SEE_string *name;
|
|
||||||
|
|
||||||
/* Create the navigator browser object. Add it to the global space */
|
|
||||||
navigator = SEE_Object_new(see);
|
|
||||||
SEE_SET_OBJECT(&value, navigator);
|
|
||||||
name = SEE_string_sprintf(see, "navigator");
|
|
||||||
SEE_OBJECT_PUT(see, see->Global, name, &value, SEE_ATTR_READONLY);
|
|
||||||
|
|
||||||
/* Create a string and attach as 'ELinks.version' */
|
|
||||||
SEE_SET_STRING(&value, SEE_string_sprintf(see, VERSION));
|
|
||||||
name = SEE_string_sprintf(see, "appVersion");
|
|
||||||
SEE_OBJECT_PUT(see, navigator, 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, "appHome");
|
|
||||||
SEE_OBJECT_PUT(see, navigator, name, &value, SEE_ATTR_READONLY);
|
|
||||||
|
|
||||||
/* Create an 'alert' method and attach to the browser object. */
|
|
||||||
/* FIXME: The browser object and the Global object should be identical. */
|
|
||||||
name = SEE_string_sprintf(see, "alert");
|
|
||||||
obj = SEE_cfunction_make(see, navigator_alert, name, 1);
|
|
||||||
SEE_SET_OBJECT(&value, obj);
|
|
||||||
SEE_OBJECT_PUT(see, navigator, name, &value, 0);
|
|
||||||
SEE_OBJECT_PUT(see, see->Global, name, &value, 0);
|
|
||||||
|
|
||||||
name = SEE_string_sprintf(see, "preference");
|
|
||||||
obj = SEE_cfunction_make(see, navigator_preference, name, 1);
|
|
||||||
SEE_SET_OBJECT(&value, obj);
|
|
||||||
SEE_OBJECT_PUT(see, navigator, name, &value, 0);
|
|
||||||
|
|
||||||
name = SEE_string_sprintf(see, "savePreferences");
|
|
||||||
obj = SEE_cfunction_make(see, navigator_save_preferences, name, 1);
|
|
||||||
SEE_SET_OBJECT(&value, obj);
|
|
||||||
SEE_OBJECT_PUT(see, navigator, name, &value, 0);
|
|
||||||
}
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
|||||||
#ifndef EL__SCRIPTING_SEE_INTERFACE_H
|
|
||||||
#define EL__SCRIPTING_SEE_INTERFACE_H
|
|
||||||
|
|
||||||
struct SEE_interpreter;
|
|
||||||
|
|
||||||
void init_see_interface(struct SEE_interpreter *see);
|
|
||||||
|
|
||||||
|
|
||||||
#endif
|
|
@ -1,22 +0,0 @@
|
|||||||
/* Simple Ecmascript Engine (SEE) browser scripting module */
|
|
||||||
|
|
||||||
#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
|
|
||||||
);
|
|
@ -1,9 +0,0 @@
|
|||||||
|
|
||||||
#ifndef EL__SCRIPTING_SEE_SEE_H
|
|
||||||
#define EL__SCRIPTING_SEE_SEE_H
|
|
||||||
|
|
||||||
struct module;
|
|
||||||
|
|
||||||
extern struct module see_scripting_module;
|
|
||||||
|
|
||||||
#endif
|
|
Loading…
Reference in New Issue
Block a user