mirror of
https://github.com/rkd77/elinks.git
synced 2024-12-04 14:46:47 -05:00
Additional functionality for Python backend.
This commit is contained in:
parent
b52583902f
commit
ebadc9bf9e
@ -1,5 +1,29 @@
|
|||||||
import re
|
"""Example Python hooks for ELinks.
|
||||||
import sys
|
|
||||||
|
If ELinks is compiled with an embedded Python interpreter, it will try
|
||||||
|
to import a Python module called hooks when the browser starts up. To
|
||||||
|
use Python code from within ELinks, create a file called hooks.py in
|
||||||
|
the ~/.elinks directory, or in the system-wide configuration directory
|
||||||
|
(defined when ELinks was compiled), or in the standard Python search path.
|
||||||
|
An example hooks.py file can be found in the contrib/python directory of
|
||||||
|
the ELinks source distribution.
|
||||||
|
|
||||||
|
The hooks module may implement any of several functions that will be
|
||||||
|
called automatically by ELinks in appropriate circumstances; it may also
|
||||||
|
bind ELinks keystrokes to callable Python objects so that arbitrary Python
|
||||||
|
code can be invoked at the whim of the user.
|
||||||
|
|
||||||
|
Functions that will be automatically called by ELinks (if they're defined):
|
||||||
|
|
||||||
|
follow_url_hook() -- Rewrite a URL for a link that's about to be followed.
|
||||||
|
goto_url_hook() -- Rewrite a URL received from a "Go to URL" dialog box.
|
||||||
|
pre_format_html_hook() -- Rewrite a document's body before it's formatted.
|
||||||
|
proxy_for_hook() -- Determine what proxy server to use for a given URL.
|
||||||
|
quit_hook() -- Clean up before ELinks exits.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
import elinks
|
||||||
|
|
||||||
dumbprefixes = {
|
dumbprefixes = {
|
||||||
"7th" : "http://7thguard.net/",
|
"7th" : "http://7thguard.net/",
|
||||||
@ -22,39 +46,221 @@ dumbprefixes = {
|
|||||||
"sd" : "http://www.slashdot.org/"
|
"sd" : "http://www.slashdot.org/"
|
||||||
}
|
}
|
||||||
|
|
||||||
cygwin = re.compile("cygwin\.com")
|
|
||||||
cygwin_sub1 = re.compile('<body bgcolor="#000000" color="#000000"')
|
|
||||||
cygwin_sub2 = '<body bgcolor="#ffffff" color="#000000"'
|
|
||||||
mbank = re.compile('^https://www\.mbank\.com\.pl/ib_navibar_3\.asp')
|
|
||||||
mbank_sub1 = re.compile('<td valign="top"><img')
|
|
||||||
mbank_sub2 = '<tr><td valign="top"><img'
|
|
||||||
google_redirect = re.compile('^http://www\.google\.com/url\?sa=D&q=(.*)')
|
|
||||||
|
|
||||||
def goto_url_hook(url, current_url):
|
def goto_url_hook(url, current_url):
|
||||||
global dumbprefixes
|
"""Rewrite a URL that was entered in a "Go to URL" dialog box.
|
||||||
|
|
||||||
if dumbprefixes.has_key(url):
|
This function should return a URL for ELinks to follow, or None if
|
||||||
return dumbprefixes[url];
|
ELinks should follow the original URL.
|
||||||
else:
|
|
||||||
return None
|
Arguments:
|
||||||
|
|
||||||
|
url -- The URL provided by the user.
|
||||||
|
current_url -- The URL of the document being viewed, or None if no
|
||||||
|
document is being viewed.
|
||||||
|
|
||||||
|
"""
|
||||||
|
if url in dumbprefixes:
|
||||||
|
return dumbprefixes[url]
|
||||||
|
|
||||||
def follow_url_hook(url):
|
def follow_url_hook(url):
|
||||||
m = google_redirect.search(url)
|
"""Rewrite a URL for a link that's about to be followed.
|
||||||
if m:
|
|
||||||
return m.group(1)
|
This function should return a URL for ELinks to follow, or None if
|
||||||
return None
|
ELinks should follow the original URL.
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
|
||||||
|
url -- The URL of the link.
|
||||||
|
|
||||||
|
"""
|
||||||
|
google_redirect = 'http://www.google.com/url?sa=D&q='
|
||||||
|
if url.startswith(google_redirect):
|
||||||
|
return url.replace(google_redirect, '')
|
||||||
|
|
||||||
def pre_format_html_hook(url, html):
|
def pre_format_html_hook(url, html):
|
||||||
if cygwin.search(url):
|
"""Rewrite the body of a document before it's formatted.
|
||||||
html2 = cygwin_sub1.sub(cygwin_sub2, html)
|
|
||||||
return html2
|
This function should return a string for ELinks to format, or None
|
||||||
if mbank.search(url):
|
if ELinks should format the original document body. It can be used
|
||||||
html2 = mbank_sub1.sub(mbank_sub2, html)
|
to repair bad HTML, alter the layout or colors of a document, etc.
|
||||||
return html2
|
|
||||||
return None
|
Arguments:
|
||||||
|
|
||||||
|
url -- The URL of the document.
|
||||||
|
html -- The body of the document.
|
||||||
|
|
||||||
|
"""
|
||||||
|
if "cygwin.com" in url:
|
||||||
|
return html.replace('<body bgcolor="#000000" color="#000000"',
|
||||||
|
'<body bgcolor="#ffffff" color="#000000"')
|
||||||
|
elif url.startswith("https://www.mbank.com.pl/ib_navibar_3.asp"):
|
||||||
|
return html.replace('<td valign="top"><img',
|
||||||
|
'<tr><td valign="top"><img')
|
||||||
|
|
||||||
def proxy_for_hook(url):
|
def proxy_for_hook(url):
|
||||||
return None
|
"""Determine what proxy server to use for a given URL.
|
||||||
|
|
||||||
|
This function should return a string of the form "hostname:portnumber"
|
||||||
|
identifying a proxy server to use, or an empty string if the request
|
||||||
|
shouldn't use any proxy server, or None if ELinks should use its
|
||||||
|
default proxy server.
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
|
||||||
|
url -- The URL that is about to be followed.
|
||||||
|
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
def quit_hook():
|
def quit_hook():
|
||||||
return None
|
"""Clean up before ELinks exits.
|
||||||
|
|
||||||
|
This function should handle any clean-up tasks that need to be
|
||||||
|
performed before ELinks exits. Its return value is ignored.
|
||||||
|
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
# The rest of this file demonstrates some of the functionality available
|
||||||
|
# within hooks.py through the embedded Python interpreter's elinks module.
|
||||||
|
# Full documentation for the elinks module (as well as the hooks module)
|
||||||
|
# can be found in doc/python.txt in the ELinks source distribution.
|
||||||
|
|
||||||
|
|
||||||
|
# This class shows how to use elinks.input_box() and elinks.open(). It
|
||||||
|
# creates a dialog box to prompt the user for a URL, and provides a callback
|
||||||
|
# function that opens the URL in a new tab.
|
||||||
|
#
|
||||||
|
class goto_url_in_new_tab:
|
||||||
|
"""Prompter that opens a given URL in a new tab."""
|
||||||
|
def __init__(self):
|
||||||
|
"""Prompt for a URL."""
|
||||||
|
self.current_location = elinks.current_url()
|
||||||
|
elinks.input_box("Enter URL", self._callback, title="Go to URL")
|
||||||
|
def _callback(self, url):
|
||||||
|
"""Open the given URL in a new tab."""
|
||||||
|
if 'goto_url_hook' in globals():
|
||||||
|
# Mimic the standard "Go to URL" dialog by calling goto_url_hook().
|
||||||
|
url = goto_url_hook(url, self.current_location) or url
|
||||||
|
if url:
|
||||||
|
elinks.open(url, new_tab=True)
|
||||||
|
# The elinks.bind_key() function can be used to create a keystroke binding
|
||||||
|
# that will instantiate the class.
|
||||||
|
#
|
||||||
|
elinks.bind_key("Ctrl-g", goto_url_in_new_tab)
|
||||||
|
|
||||||
|
|
||||||
|
# This class can be used to enter arbitrary Python expressions for the embedded
|
||||||
|
# interpreter to evaluate. (Note that it can only evalute Python expressions,
|
||||||
|
# not execute arbitrary Python code.) The callback function will use
|
||||||
|
# elinks.info_box() to display the results.
|
||||||
|
#
|
||||||
|
class simple_console:
|
||||||
|
"""Simple console for passing expressions to the Python interpreter."""
|
||||||
|
def __init__(self):
|
||||||
|
"""Prompt for a Python expression."""
|
||||||
|
elinks.input_box("Enter Python expression", self._callback, "Console")
|
||||||
|
def _callback(self, input):
|
||||||
|
"""Evalute input and display the result (unless it's None)."""
|
||||||
|
if input is None:
|
||||||
|
# The user canceled.
|
||||||
|
return
|
||||||
|
result = eval(input)
|
||||||
|
if result is not None:
|
||||||
|
elinks.info_box(result, "Result")
|
||||||
|
elinks.bind_key("F5", simple_console)
|
||||||
|
|
||||||
|
|
||||||
|
# If you edit ~/.elinks/hooks.py while the browser is running, you can use
|
||||||
|
# this function to make your changes take effect immediately (without having
|
||||||
|
# to restart ELinks).
|
||||||
|
#
|
||||||
|
def reload_hooks():
|
||||||
|
"""Reload the ELinks Python hooks."""
|
||||||
|
import hooks
|
||||||
|
reload(hooks)
|
||||||
|
elinks.bind_key("F6", reload_hooks)
|
||||||
|
|
||||||
|
|
||||||
|
# This example demonstrates how to create a menu by providing a sequence of
|
||||||
|
# (string, function) tuples specifying each menu item.
|
||||||
|
#
|
||||||
|
def menu():
|
||||||
|
"""Let the user choose from a menu of Python functions."""
|
||||||
|
items = (
|
||||||
|
("~Go to URL in new tab", goto_url_in_new_tab),
|
||||||
|
("Simple Python ~console", simple_console),
|
||||||
|
("~Reload Python hooks", reload_hooks),
|
||||||
|
("Read my favorite RSS/ATOM ~feeds", feedreader),
|
||||||
|
)
|
||||||
|
elinks.menu(items)
|
||||||
|
elinks.bind_key("F4", menu)
|
||||||
|
|
||||||
|
|
||||||
|
# Finally, a more elaborate demonstration: If you install the add-on Python
|
||||||
|
# module from http://feedparser.org/ this class can use it to parse a list
|
||||||
|
# of your favorite RSS/ATOM feeds, figure out which entries you haven't seen
|
||||||
|
# yet, open each of those entries in a new tab, and report statistics on what
|
||||||
|
# it fetched. The class is instantiated by pressing the "!" key.
|
||||||
|
#
|
||||||
|
# This class demonstrates the elinks.load() function, which can be used to
|
||||||
|
# load a document into the ELinks cache without displaying it to the user;
|
||||||
|
# the document's contents are passed to a Python callback function.
|
||||||
|
|
||||||
|
my_favorite_feeds = (
|
||||||
|
"http://rss.gmane.org/messages/excerpts/gmane.comp.web.elinks.user",
|
||||||
|
# ... add any other RSS or ATOM feeds that interest you ...
|
||||||
|
# "http://elinks.cz/news.rss",
|
||||||
|
# "http://www.python.org/channews.rdf",
|
||||||
|
)
|
||||||
|
|
||||||
|
class feedreader:
|
||||||
|
|
||||||
|
"""RSS/ATOM feed reader."""
|
||||||
|
|
||||||
|
def __init__(self, feeds=my_favorite_feeds):
|
||||||
|
"""Constructor."""
|
||||||
|
self._results = {}
|
||||||
|
self._feeds = feeds
|
||||||
|
for feed in feeds:
|
||||||
|
elinks.load(feed, self._callback)
|
||||||
|
|
||||||
|
def _callback(self, header, body):
|
||||||
|
"""Read a feed, identify unseen entries, and open them in new tabs."""
|
||||||
|
import anydbm
|
||||||
|
import feedparser # you need to get this module from feedparser.org
|
||||||
|
import os
|
||||||
|
|
||||||
|
if not body:
|
||||||
|
return
|
||||||
|
seen = anydbm.open(os.path.join(elinks.home, "rss.seen"), "c")
|
||||||
|
feed = feedparser.parse(body)
|
||||||
|
new = 0
|
||||||
|
errors = 0
|
||||||
|
for entry in feed.entries:
|
||||||
|
try:
|
||||||
|
if not seen.has_key(entry.link):
|
||||||
|
elinks.open(entry.link, new_tab=True)
|
||||||
|
seen[entry.link] = ""
|
||||||
|
new += 1
|
||||||
|
except:
|
||||||
|
errors += 1
|
||||||
|
seen.close()
|
||||||
|
self._tally(feed.channel.title, new, errors)
|
||||||
|
|
||||||
|
def _tally(self, title, new, errors):
|
||||||
|
"""Record and report feed statistics."""
|
||||||
|
self._results[title] = (new, errors)
|
||||||
|
if len(self._results) == len(self._feeds):
|
||||||
|
feeds = self._results.keys()
|
||||||
|
feeds.sort()
|
||||||
|
width = max([len(title) for title in feeds])
|
||||||
|
fmt = "%*s new entries: %2d errors: %2d\n"
|
||||||
|
summary = ""
|
||||||
|
for title in feeds:
|
||||||
|
new, errors = self._results[title]
|
||||||
|
summary += fmt % (-width, title, new, errors)
|
||||||
|
elinks.info_box(summary, "Feed Statistics")
|
||||||
|
|
||||||
|
elinks.bind_key("!", feedreader)
|
||||||
|
@ -3,6 +3,15 @@ include $(top_builddir)/Makefile.config
|
|||||||
|
|
||||||
INCLUDES += $(PYTHON_CFLAGS)
|
INCLUDES += $(PYTHON_CFLAGS)
|
||||||
|
|
||||||
OBJS = python.o hooks.o core.o
|
OBJS = \
|
||||||
|
core.o \
|
||||||
|
dialogs.o \
|
||||||
|
document.o \
|
||||||
|
hooks.o \
|
||||||
|
keybinding.o \
|
||||||
|
load.o \
|
||||||
|
menu.o \
|
||||||
|
open.o \
|
||||||
|
python.o
|
||||||
|
|
||||||
include $(top_srcdir)/Makefile.lib
|
include $(top_srcdir)/Makefile.lib
|
||||||
|
@ -5,37 +5,42 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <Python.h>
|
#include <Python.h>
|
||||||
|
#include <osdefs.h>
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
#include "elinks.h"
|
#include "elinks.h"
|
||||||
|
|
||||||
#include "config/home.h"
|
#include "config/home.h"
|
||||||
#include "main/module.h"
|
#include "main/module.h"
|
||||||
#include "scripting/scripting.h"
|
|
||||||
#include "scripting/python/core.h"
|
#include "scripting/python/core.h"
|
||||||
|
#include "scripting/python/dialogs.h"
|
||||||
|
#include "scripting/python/document.h"
|
||||||
|
#include "scripting/python/keybinding.h"
|
||||||
|
#include "scripting/python/load.h"
|
||||||
|
#include "scripting/python/menu.h"
|
||||||
|
#include "scripting/python/open.h"
|
||||||
#include "scripting/python/python.h"
|
#include "scripting/python/python.h"
|
||||||
|
#include "scripting/scripting.h"
|
||||||
|
#include "session/session.h"
|
||||||
#include "util/env.h"
|
#include "util/env.h"
|
||||||
#include "util/file.h"
|
|
||||||
#include "util/string.h"
|
#include "util/string.h"
|
||||||
|
|
||||||
|
struct session *python_ses = NULL;
|
||||||
|
|
||||||
PyObject *pDict = NULL, *pModule = NULL;
|
PyObject *python_elinks_err = NULL;
|
||||||
|
PyObject *python_hooks = NULL;
|
||||||
|
|
||||||
/* Error reporting. */
|
/* Error reporting. */
|
||||||
|
|
||||||
void
|
void
|
||||||
alert_python_error(struct session *ses)
|
alert_python_error(void)
|
||||||
{
|
{
|
||||||
unsigned char *msg = "(no traceback available)";
|
unsigned char *msg = "(no traceback available)";
|
||||||
PyObject *err_type = NULL, *err_value = NULL, *err_traceback = NULL;
|
PyObject *err_type = NULL, *err_value = NULL, *err_traceback = NULL;
|
||||||
PyObject *tb_module = NULL;
|
PyObject *tb_module = NULL;
|
||||||
PyObject *tb_dict;
|
|
||||||
PyObject *format_function;
|
|
||||||
PyObject *msg_list = NULL;
|
PyObject *msg_list = NULL;
|
||||||
PyObject *empty_string = NULL;
|
PyObject *empty_string = NULL;
|
||||||
PyObject *join_method = NULL;
|
|
||||||
PyObject *msg_string = NULL;
|
PyObject *msg_string = NULL;
|
||||||
unsigned char *temp;
|
unsigned char *temp;
|
||||||
|
|
||||||
@ -46,17 +51,15 @@ alert_python_error(struct session *ses)
|
|||||||
*/
|
*/
|
||||||
PyErr_Fetch(&err_type, &err_value, &err_traceback);
|
PyErr_Fetch(&err_type, &err_value, &err_traceback);
|
||||||
PyErr_NormalizeException(&err_type, &err_value, &err_traceback);
|
PyErr_NormalizeException(&err_type, &err_value, &err_traceback);
|
||||||
if (!err_traceback) goto end;
|
if (!err_type) goto end;
|
||||||
|
|
||||||
tb_module = PyImport_ImportModule("traceback");
|
tb_module = PyImport_ImportModule("traceback");
|
||||||
if (!tb_module) goto end;
|
if (!tb_module) goto end;
|
||||||
|
|
||||||
tb_dict = PyModule_GetDict(tb_module);
|
msg_list = PyObject_CallMethod(tb_module, "format_exception", "OOO",
|
||||||
format_function = PyDict_GetItemString(tb_dict, "format_exception");
|
err_type,
|
||||||
if (!format_function || !PyCallable_Check(format_function)) goto end;
|
err_value ? err_value : Py_None,
|
||||||
|
err_traceback ? err_traceback : Py_None);
|
||||||
msg_list = PyObject_CallFunction(format_function, "OOO",
|
|
||||||
err_type, err_value, err_traceback);
|
|
||||||
if (!msg_list) goto end;
|
if (!msg_list) goto end;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -67,17 +70,14 @@ alert_python_error(struct session *ses)
|
|||||||
empty_string = PyString_FromString("");
|
empty_string = PyString_FromString("");
|
||||||
if (!empty_string) goto end;
|
if (!empty_string) goto end;
|
||||||
|
|
||||||
join_method = PyObject_GetAttrString(empty_string, "join");
|
msg_string = PyObject_CallMethod(empty_string, "join", "O", msg_list);
|
||||||
if (!join_method || !PyCallable_Check(join_method)) goto end;
|
|
||||||
|
|
||||||
msg_string = PyObject_CallFunction(join_method, "O", msg_list);
|
|
||||||
if (!msg_string) goto end;
|
if (!msg_string) goto end;
|
||||||
|
|
||||||
temp = (unsigned char *) PyString_AsString(msg_string);
|
temp = (unsigned char *) PyString_AsString(msg_string);
|
||||||
if (temp) msg = temp;
|
if (temp) msg = temp;
|
||||||
|
|
||||||
end:
|
end:
|
||||||
report_scripting_error(&python_scripting_module, ses, msg);
|
report_scripting_error(&python_scripting_module, python_ses, msg);
|
||||||
|
|
||||||
Py_XDECREF(err_type);
|
Py_XDECREF(err_type);
|
||||||
Py_XDECREF(err_value);
|
Py_XDECREF(err_value);
|
||||||
@ -85,31 +85,69 @@ end:
|
|||||||
Py_XDECREF(tb_module);
|
Py_XDECREF(tb_module);
|
||||||
Py_XDECREF(msg_list);
|
Py_XDECREF(msg_list);
|
||||||
Py_XDECREF(empty_string);
|
Py_XDECREF(empty_string);
|
||||||
Py_XDECREF(join_method);
|
|
||||||
Py_XDECREF(msg_string);
|
Py_XDECREF(msg_string);
|
||||||
|
|
||||||
/* In case another error occurred while reporting the original error: */
|
/* In case another error occurred while reporting the original error: */
|
||||||
PyErr_Clear();
|
PyErr_Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
/* Prepend ELinks directories to Python's search path. */
|
||||||
cleanup_python(struct module *module)
|
|
||||||
|
static int
|
||||||
|
set_python_search_path(void)
|
||||||
{
|
{
|
||||||
if (Py_IsInitialized()) {
|
struct string new_python_path, *okay;
|
||||||
Py_XDECREF(pDict);
|
unsigned char *old_python_path;
|
||||||
Py_XDECREF(pModule);
|
int result = -1;
|
||||||
Py_Finalize();
|
|
||||||
}
|
if (!init_string(&new_python_path)) return result;
|
||||||
|
|
||||||
|
old_python_path = (unsigned char *) getenv("PYTHONPATH");
|
||||||
|
if (old_python_path)
|
||||||
|
okay = add_format_to_string(&new_python_path, "%s%c%s%c%s",
|
||||||
|
elinks_home, DELIM, CONFDIR,
|
||||||
|
DELIM, old_python_path);
|
||||||
|
else
|
||||||
|
okay = add_format_to_string(&new_python_path, "%s%c%s",
|
||||||
|
elinks_home, DELIM, CONFDIR);
|
||||||
|
if (okay) result = env_set("PYTHONPATH", new_python_path.source, -1);
|
||||||
|
done_string(&new_python_path);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Module-level documentation for the Python interpreter's elinks module. */
|
||||||
|
|
||||||
|
static char module_doc[] =
|
||||||
|
PYTHON_DOCSTRING("Interface to the ELinks web browser.\n\
|
||||||
|
\n\
|
||||||
|
Functions:\n\
|
||||||
|
\n\
|
||||||
|
bind_key() -- Bind a keystroke to a callable object.\n\
|
||||||
|
current_document() -- Return the body of the document being viewed.\n\
|
||||||
|
current_header() -- Return the header of the document being viewed.\n\
|
||||||
|
current_link_url() -- Return the URL of the currently selected link.\n\
|
||||||
|
current_title() -- Return the title of the document being viewed.\n\
|
||||||
|
current_url() -- Return the URL of the document being viewed.\n\
|
||||||
|
info_box() -- Display information to the user.\n\
|
||||||
|
input_box() -- Prompt for user input.\n\
|
||||||
|
load() -- Load a document into the ELinks cache.\n\
|
||||||
|
menu() -- Display a menu.\n\
|
||||||
|
open() -- View a document.\n\
|
||||||
|
\n\
|
||||||
|
Exception classes:\n\
|
||||||
|
\n\
|
||||||
|
error -- Errors internal to ELinks.\n\
|
||||||
|
\n\
|
||||||
|
Other public objects:\n\
|
||||||
|
\n\
|
||||||
|
home -- A string containing the pathname of the ~/.elinks directory.\n");
|
||||||
|
|
||||||
void
|
void
|
||||||
init_python(struct module *module)
|
init_python(struct module *module)
|
||||||
{
|
{
|
||||||
unsigned char *python_path = straconcat(elinks_home, ":", CONFDIR, NULL);
|
PyObject *elinks_module, *module_dict, *module_name;
|
||||||
|
|
||||||
if (!python_path) return;
|
if (set_python_search_path() != 0) return;
|
||||||
env_set("PYTHONPATH", python_path, -1);
|
|
||||||
mem_free(python_path);
|
|
||||||
|
|
||||||
/* Treat warnings as errors so they can be caught and handled;
|
/* Treat warnings as errors so they can be caught and handled;
|
||||||
* otherwise they would be printed to stderr.
|
* otherwise they would be printed to stderr.
|
||||||
@ -123,12 +161,66 @@ init_python(struct module *module)
|
|||||||
PySys_AddWarnOption("error");
|
PySys_AddWarnOption("error");
|
||||||
|
|
||||||
Py_Initialize();
|
Py_Initialize();
|
||||||
pModule = PyImport_ImportModule("hooks");
|
|
||||||
|
|
||||||
if (pModule) {
|
elinks_module = Py_InitModule3("elinks", NULL, module_doc);
|
||||||
pDict = PyModule_GetDict(pModule);
|
if (!elinks_module) goto python_error;
|
||||||
Py_INCREF(pDict);
|
|
||||||
} else {
|
if (PyModule_AddStringConstant(elinks_module, "home", elinks_home) != 0)
|
||||||
alert_python_error(NULL);
|
goto python_error;
|
||||||
|
|
||||||
|
python_elinks_err = PyErr_NewException("elinks.error", NULL, NULL);
|
||||||
|
if (!python_elinks_err) goto python_error;
|
||||||
|
|
||||||
|
if (PyModule_AddObject(elinks_module, "error", python_elinks_err) != 0)
|
||||||
|
goto python_error;
|
||||||
|
|
||||||
|
module_dict = PyModule_GetDict(elinks_module);
|
||||||
|
if (!module_dict) goto python_error;
|
||||||
|
module_name = PyString_FromString("elinks");
|
||||||
|
if (!module_name) goto python_error;
|
||||||
|
|
||||||
|
if (python_init_dialogs_interface(module_dict, module_name) != 0
|
||||||
|
|| python_init_document_interface(module_dict, module_name) != 0
|
||||||
|
|| python_init_keybinding_interface(module_dict, module_name) != 0
|
||||||
|
|| python_init_load_interface(module_dict, module_name) != 0
|
||||||
|
|| python_init_menu_interface(module_dict, module_name) != 0
|
||||||
|
|| python_init_open_interface(module_dict, module_name) != 0)
|
||||||
|
goto python_error;
|
||||||
|
|
||||||
|
python_hooks = PyImport_ImportModule("hooks");
|
||||||
|
if (!python_hooks) goto python_error;
|
||||||
|
|
||||||
|
return;
|
||||||
|
|
||||||
|
python_error:
|
||||||
|
alert_python_error();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
cleanup_python(struct module *module)
|
||||||
|
{
|
||||||
|
if (Py_IsInitialized()) {
|
||||||
|
python_done_keybinding_interface();
|
||||||
|
Py_XDECREF(python_hooks);
|
||||||
|
Py_Finalize();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Add methods to a Python extension module. */
|
||||||
|
|
||||||
|
int
|
||||||
|
add_python_methods(PyObject *dict, PyObject *name, PyMethodDef *methods)
|
||||||
|
{
|
||||||
|
PyMethodDef *method;
|
||||||
|
|
||||||
|
for (method = methods; method && method->ml_name; method++) {
|
||||||
|
PyObject *function = PyCFunction_NewEx(method, NULL, name);
|
||||||
|
int result;
|
||||||
|
|
||||||
|
if (!function) return -1;
|
||||||
|
result = PyDict_SetItemString(dict, method->ml_name, function);
|
||||||
|
Py_DECREF(function);
|
||||||
|
if (result != 0) return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
@ -2,11 +2,26 @@
|
|||||||
#ifndef EL__SCRIPTING_PYTHON_CORE_H
|
#ifndef EL__SCRIPTING_PYTHON_CORE_H
|
||||||
#define EL__SCRIPTING_PYTHON_CORE_H
|
#define EL__SCRIPTING_PYTHON_CORE_H
|
||||||
|
|
||||||
struct module;
|
#ifdef HAVE_CONFIG_H
|
||||||
struct session;
|
#include "config.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct module;
|
||||||
|
|
||||||
|
extern struct session *python_ses;
|
||||||
|
extern PyObject *python_elinks_err;
|
||||||
|
|
||||||
|
void alert_python_error(void);
|
||||||
|
|
||||||
void alert_python_error(struct session *ses);
|
|
||||||
void init_python(struct module *module);
|
void init_python(struct module *module);
|
||||||
void cleanup_python(struct module *module);
|
void cleanup_python(struct module *module);
|
||||||
|
|
||||||
|
int add_python_methods(PyObject *dict, PyObject *name, PyMethodDef *methods);
|
||||||
|
|
||||||
|
#ifndef CONFIG_SMALL
|
||||||
|
#define PYTHON_DOCSTRING(str) str
|
||||||
|
#else
|
||||||
|
#define PYTHON_DOCSTRING(str) ""
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -4,6 +4,10 @@
|
|||||||
#include "config.h"
|
#include "config.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include <Python.h>
|
||||||
|
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
#include "elinks.h"
|
#include "elinks.h"
|
||||||
|
|
||||||
@ -11,133 +15,89 @@
|
|||||||
#include "main/event.h"
|
#include "main/event.h"
|
||||||
#include "protocol/uri.h"
|
#include "protocol/uri.h"
|
||||||
#include "scripting/python/core.h"
|
#include "scripting/python/core.h"
|
||||||
#include "scripting/python/hooks.h"
|
|
||||||
#include "session/location.h"
|
#include "session/location.h"
|
||||||
#include "session/session.h"
|
#include "session/session.h"
|
||||||
|
#include "util/memory.h"
|
||||||
#include "util/string.h"
|
#include "util/string.h"
|
||||||
|
|
||||||
#undef _POSIX_C_SOURCE
|
extern PyObject *python_hooks;
|
||||||
#include <Python.h>
|
|
||||||
|
|
||||||
/* The events that will trigger the functions below and what they are expected
|
/*
|
||||||
* to do is explained in doc/events.txt */
|
* A utility function for script_hook_url() and script_hook_get_proxy():
|
||||||
|
* Free a char * and replace it with the contents of a Python string.
|
||||||
|
* (Py_None is ignored.)
|
||||||
|
*/
|
||||||
|
|
||||||
extern PyObject *pDict;
|
static PyObject *
|
||||||
extern PyObject *pModule;
|
replace_with_python_string(unsigned char **dest, PyObject *object)
|
||||||
|
|
||||||
static void
|
|
||||||
do_script_hook_goto_url(struct session *ses, unsigned char **url)
|
|
||||||
{
|
{
|
||||||
PyObject *pFunc = PyDict_GetItemString(pDict, "goto_url_hook");
|
unsigned char *str;
|
||||||
|
|
||||||
if (pFunc && PyCallable_Check(pFunc)) {
|
if (object == Py_None) return object;
|
||||||
PyObject *pValue;
|
|
||||||
unsigned char *current_url;
|
|
||||||
|
|
||||||
if (!ses || !have_location(ses)) {
|
str = (unsigned char *) PyString_AsString(object);
|
||||||
current_url = NULL;
|
if (!str) return NULL;
|
||||||
} else {
|
|
||||||
|
str = stracpy(str);
|
||||||
|
if (!str) return PyErr_NoMemory();
|
||||||
|
|
||||||
|
mem_free_set(dest, str);
|
||||||
|
return object;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Call a Python hook for a goto-url or follow-url event. */
|
||||||
|
|
||||||
|
static enum evhook_status
|
||||||
|
script_hook_url(va_list ap, void *data)
|
||||||
|
{
|
||||||
|
unsigned char **url = va_arg(ap, unsigned char **);
|
||||||
|
struct session *ses = va_arg(ap, struct session *);
|
||||||
|
char *method = data;
|
||||||
|
struct session *saved_python_ses = python_ses;
|
||||||
|
PyObject *result;
|
||||||
|
|
||||||
|
evhook_use_params(url && ses);
|
||||||
|
|
||||||
|
if (!python_hooks || !url || !*url
|
||||||
|
|| !PyObject_HasAttrString(python_hooks, method))
|
||||||
|
return EVENT_HOOK_STATUS_NEXT;
|
||||||
|
|
||||||
|
python_ses = ses;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Historical note: The only reason the goto and follow hooks are
|
||||||
|
* treated differently is to maintain backwards compatibility for
|
||||||
|
* people who already have a goto_url_hook() function in hooks.py
|
||||||
|
* that expects a second argument. If we were starting over from
|
||||||
|
* scratch, we could treat the goto and follow hooks identically and
|
||||||
|
* simply pass @url as the sole argument in both cases; the Python
|
||||||
|
* code for the goto hook no longer needs its @current_url argument
|
||||||
|
* since it could instead determine the current URL by calling the
|
||||||
|
* Python interpreter's elinks.current_url() function.
|
||||||
|
*/
|
||||||
|
if (!strcmp(method, "goto_url_hook")) {
|
||||||
|
unsigned char *current_url = NULL;
|
||||||
|
|
||||||
|
if (python_ses && have_location(python_ses))
|
||||||
current_url = struri(cur_loc(ses)->vs.uri);
|
current_url = struri(cur_loc(ses)->vs.uri);
|
||||||
}
|
|
||||||
|
|
||||||
pValue = PyObject_CallFunction(pFunc, "ss", *url, current_url);
|
result = PyObject_CallMethod(python_hooks, method, "ss", *url,
|
||||||
if (pValue) {
|
current_url);
|
||||||
if (pValue != Py_None) {
|
|
||||||
const unsigned char *str;
|
|
||||||
unsigned char *new_url;
|
|
||||||
|
|
||||||
str = PyString_AsString(pValue);
|
|
||||||
if (str) {
|
|
||||||
new_url = stracpy((unsigned char *)str);
|
|
||||||
if (new_url) mem_free_set(url, new_url);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Py_DECREF(pValue);
|
|
||||||
} else {
|
} else {
|
||||||
alert_python_error(ses);
|
result = PyObject_CallMethod(python_hooks, method, "s", *url);
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static enum evhook_status
|
if (!result || !replace_with_python_string(url, result))
|
||||||
script_hook_goto_url(va_list ap, void *data)
|
alert_python_error();
|
||||||
{
|
|
||||||
unsigned char **url = va_arg(ap, unsigned char **);
|
|
||||||
struct session *ses = va_arg(ap, struct session *);
|
|
||||||
|
|
||||||
if (pDict && *url)
|
Py_XDECREF(result);
|
||||||
do_script_hook_goto_url(ses, url);
|
|
||||||
|
python_ses = saved_python_ses;
|
||||||
|
|
||||||
return EVENT_HOOK_STATUS_NEXT;
|
return EVENT_HOOK_STATUS_NEXT;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
/* Call a Python hook for a pre-format-html event. */
|
||||||
do_script_hook_follow_url(struct session *ses, unsigned char **url)
|
|
||||||
{
|
|
||||||
PyObject *pFunc = PyDict_GetItemString(pDict, "follow_url_hook");
|
|
||||||
|
|
||||||
if (pFunc && PyCallable_Check(pFunc)) {
|
|
||||||
PyObject *pValue = PyObject_CallFunction(pFunc, "s", *url);
|
|
||||||
if (pValue) {
|
|
||||||
if (pValue != Py_None) {
|
|
||||||
const unsigned char *str;
|
|
||||||
unsigned char *new_url;
|
|
||||||
|
|
||||||
str = PyString_AsString(pValue);
|
|
||||||
if (str) {
|
|
||||||
new_url = stracpy((unsigned char *)str);
|
|
||||||
if (new_url) mem_free_set(url, new_url);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Py_DECREF(pValue);
|
|
||||||
} else {
|
|
||||||
alert_python_error(ses);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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 *);
|
|
||||||
|
|
||||||
if (pDict && *url)
|
|
||||||
do_script_hook_follow_url(ses, url);
|
|
||||||
|
|
||||||
return EVENT_HOOK_STATUS_NEXT;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
do_script_hook_pre_format_html(struct session *ses, unsigned char *url,
|
|
||||||
struct cache_entry *cached,
|
|
||||||
struct fragment *fragment)
|
|
||||||
{
|
|
||||||
PyObject *pFunc = PyDict_GetItemString(pDict, "pre_format_html_hook");
|
|
||||||
|
|
||||||
if (pFunc && PyCallable_Check(pFunc)) {
|
|
||||||
PyObject *pValue = PyObject_CallFunction(pFunc, "ss#", url,
|
|
||||||
fragment->data,
|
|
||||||
fragment->length);
|
|
||||||
|
|
||||||
if (pValue) {
|
|
||||||
if (pValue != Py_None) {
|
|
||||||
const unsigned char *str;
|
|
||||||
int len;
|
|
||||||
|
|
||||||
str = PyString_AsString(pValue);
|
|
||||||
if (str) {
|
|
||||||
len = PyString_Size(pValue);
|
|
||||||
add_fragment(cached, 0, str, len);
|
|
||||||
normalize_cache_entry(cached, len);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Py_DECREF(pValue);
|
|
||||||
} else {
|
|
||||||
alert_python_error(ses);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static enum evhook_status
|
static enum evhook_status
|
||||||
script_hook_pre_format_html(va_list ap, void *data)
|
script_hook_pre_format_html(va_list ap, void *data)
|
||||||
@ -146,78 +106,94 @@ script_hook_pre_format_html(va_list ap, void *data)
|
|||||||
struct cache_entry *cached = va_arg(ap, struct cache_entry *);
|
struct cache_entry *cached = va_arg(ap, struct cache_entry *);
|
||||||
struct fragment *fragment = get_cache_fragment(cached);
|
struct fragment *fragment = get_cache_fragment(cached);
|
||||||
unsigned char *url = struri(cached->uri);
|
unsigned char *url = struri(cached->uri);
|
||||||
|
char *method = "pre_format_html_hook";
|
||||||
|
struct session *saved_python_ses = python_ses;
|
||||||
|
PyObject *result;
|
||||||
|
int success = 0;
|
||||||
|
|
||||||
if (pDict && ses && url && cached->length && *fragment->data)
|
evhook_use_params(ses && cached);
|
||||||
do_script_hook_pre_format_html(ses, url, cached, fragment);
|
|
||||||
|
if (!python_hooks || !cached->length || !*fragment->data
|
||||||
|
|| !PyObject_HasAttrString(python_hooks, method))
|
||||||
|
return EVENT_HOOK_STATUS_NEXT;
|
||||||
|
|
||||||
|
python_ses = ses;
|
||||||
|
|
||||||
|
result = PyObject_CallMethod(python_hooks, method, "ss#", url,
|
||||||
|
fragment->data, fragment->length);
|
||||||
|
if (!result) goto error;
|
||||||
|
|
||||||
|
if (result != Py_None) {
|
||||||
|
unsigned char *str;
|
||||||
|
int len;
|
||||||
|
|
||||||
|
if (PyString_AsStringAndSize(result, (char **) &str, &len) != 0)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
(void) add_fragment(cached, 0, str, len);
|
||||||
|
normalize_cache_entry(cached, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
success = 1;
|
||||||
|
|
||||||
|
error:
|
||||||
|
if (!success) alert_python_error();
|
||||||
|
|
||||||
|
Py_XDECREF(result);
|
||||||
|
|
||||||
|
python_ses = saved_python_ses;
|
||||||
|
|
||||||
return EVENT_HOOK_STATUS_NEXT;
|
return EVENT_HOOK_STATUS_NEXT;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void
|
/* Call a Python hook for a get-proxy event. */
|
||||||
do_script_hook_get_proxy(unsigned char **new_proxy_url, unsigned char *url)
|
|
||||||
{
|
|
||||||
PyObject *pFunc = PyDict_GetItemString(pDict, "proxy_for_hook");
|
|
||||||
|
|
||||||
if (pFunc && PyCallable_Check(pFunc)) {
|
|
||||||
PyObject *pValue = PyObject_CallFunction(pFunc, "s", url);
|
|
||||||
|
|
||||||
if (pValue) {
|
|
||||||
if (pValue != Py_None) {
|
|
||||||
const unsigned char *str;
|
|
||||||
unsigned char *new_url;
|
|
||||||
|
|
||||||
str = PyString_AsString(pValue);
|
|
||||||
if (str) {
|
|
||||||
new_url = stracpy((unsigned char *)str);
|
|
||||||
if (new_url) mem_free_set(new_proxy_url,
|
|
||||||
new_url);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Py_DECREF(pValue);
|
|
||||||
} else {
|
|
||||||
alert_python_error(NULL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static enum evhook_status
|
static enum evhook_status
|
||||||
script_hook_get_proxy(va_list ap, void *data)
|
script_hook_get_proxy(va_list ap, void *data)
|
||||||
{
|
{
|
||||||
unsigned char **new_proxy_url = va_arg(ap, unsigned char **);
|
unsigned char **proxy = va_arg(ap, unsigned char **);
|
||||||
unsigned char *url = va_arg(ap, unsigned char *);
|
unsigned char *url = va_arg(ap, unsigned char *);
|
||||||
|
char *method = "proxy_for_hook";
|
||||||
|
PyObject *result;
|
||||||
|
|
||||||
if (pDict && new_proxy_url && url)
|
evhook_use_params(proxy && url);
|
||||||
do_script_hook_get_proxy(new_proxy_url, url);
|
|
||||||
|
if (!python_hooks || !proxy || !url
|
||||||
|
|| !PyObject_HasAttrString(python_hooks, method))
|
||||||
|
return EVENT_HOOK_STATUS_NEXT;
|
||||||
|
|
||||||
|
result = PyObject_CallMethod(python_hooks, method, "s", url);
|
||||||
|
|
||||||
|
if (!result || !replace_with_python_string(proxy, result))
|
||||||
|
alert_python_error();
|
||||||
|
|
||||||
|
Py_XDECREF(result);
|
||||||
|
|
||||||
return EVENT_HOOK_STATUS_NEXT;
|
return EVENT_HOOK_STATUS_NEXT;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
/* Call a Python hook for a quit event. */
|
||||||
do_script_hook_quit(void)
|
|
||||||
{
|
|
||||||
PyObject *pFunc = PyDict_GetItemString(pDict, "quit_hook");
|
|
||||||
|
|
||||||
if (pFunc && PyCallable_Check(pFunc)) {
|
|
||||||
PyObject *pValue = PyObject_CallFunction(pFunc, NULL);
|
|
||||||
|
|
||||||
if (pValue) {
|
|
||||||
Py_DECREF(pValue);
|
|
||||||
} else {
|
|
||||||
alert_python_error(NULL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static enum evhook_status
|
static enum evhook_status
|
||||||
script_hook_quit(va_list ap, void *data)
|
script_hook_quit(va_list ap, void *data)
|
||||||
{
|
{
|
||||||
if (pDict) do_script_hook_quit();
|
char *method = "quit_hook";
|
||||||
|
PyObject *result;
|
||||||
|
|
||||||
|
if (!python_hooks || !PyObject_HasAttrString(python_hooks, method))
|
||||||
|
return EVENT_HOOK_STATUS_NEXT;
|
||||||
|
|
||||||
|
result = PyObject_CallMethod(python_hooks, method, NULL);
|
||||||
|
if (!result) alert_python_error();
|
||||||
|
|
||||||
|
Py_XDECREF(result);
|
||||||
|
|
||||||
return EVENT_HOOK_STATUS_NEXT;
|
return EVENT_HOOK_STATUS_NEXT;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct event_hook_info python_scripting_hooks[] = {
|
struct event_hook_info python_scripting_hooks[] = {
|
||||||
{ "goto-url", 0, script_hook_goto_url, NULL },
|
{ "goto-url", 0, script_hook_url, "goto_url_hook" },
|
||||||
{ "follow-url", 0, script_hook_follow_url, NULL },
|
{ "follow-url", 0, script_hook_url, "follow_url_hook" },
|
||||||
{ "pre-format-html", 0, script_hook_pre_format_html, NULL },
|
{ "pre-format-html", 0, script_hook_pre_format_html, NULL },
|
||||||
{ "get-proxy", 0, script_hook_get_proxy, NULL },
|
{ "get-proxy", 0, script_hook_get_proxy, NULL },
|
||||||
{ "quit", 0, script_hook_quit, NULL },
|
{ "quit", 0, script_hook_quit, NULL },
|
||||||
|
@ -4,11 +4,12 @@
|
|||||||
#include "config.h"
|
#include "config.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "scripting/python/core.h"
|
#include <Python.h>
|
||||||
|
|
||||||
#include "elinks.h"
|
#include "elinks.h"
|
||||||
|
|
||||||
#include "main/module.h"
|
#include "main/module.h"
|
||||||
|
#include "scripting/python/core.h"
|
||||||
#include "scripting/python/hooks.h"
|
#include "scripting/python/hooks.h"
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user