2 Scripting ELinks with Lua
3 -------------------------
5 This file documents the Lua scripting interface of the ELinks web browser.
13 Lua scripting capabilities permit users to customize the ELinks behaviour to
14 unusual degree - they allow automatic rewriting of HTML code of the received
15 documents, rewriting of the URLs entered by user etc. You can even write your
16 own bookmarks system with Lua. See also contrib/lua/ for some examples of the
17 possibilities of ELinks Lua support.
19 Please do not confuse Lua scripting with JavaScript, EcmaScript, VBScript and
20 similar. Those are embedded in page, allowing per-document scripting related
21 to its presentation and providing some degree of interactivity etc. On the
22 contrary, the current Lua support permits scripts to be embedded to the
23 browser directly, changing the behaviour of the browser, not the document.
25 The original Lua support (in the form of Links-Lua fork of original Links) was
26 written by Peter Wang and Cliff Cunnington. There are some rough edges
27 remaining, but is suitable for everyday use (I have been using it every day
33 The Lua scripting support comes with the stock ELinks distribution, no
34 additional patches and tweaks should be needed.
36 The web site of the original Links-Lua is at
37 http://links.sourceforge.net/links-lua/[]. Some older patches against
38 regular Links are available at
39 http://www.sourceforge.net/projects/links/[], but they are not being
42 Lua can be found at http://www.lua.org/[].
47 The Lua support has only been tested under Linux, although it *should*
48 work under other platforms that ELinks and Lua support (perhaps with some
49 changes to source code?).
51 Also, note that many of the scripts given here assume a Unix system.
52 Your mileage will definitely vary on other platforms.
61 Before you can compile ELinks with Lua support, you must compile and install
62 Lua. The following instructions are for a Linux system. People on other
63 systems should try to enable `popen` support, but this is not necessary
64 (you will lose a bit of functionality though).
66 1. Download and unpack the Lua `tar.gz` or `zip` somewhere.
67 2. `cd` into the `lua` directory.
68 3. Open `config` in a text editor and uncomment the `POPEN` line.
69 4. Optionally, change the `INSTALL_ROOT line.
70 5. Run `make; make so; make sobin; make install`.
72 On systems without shared object support, simply run `make; make install`
75 Since ELinks 0.11.0, only version 5.0 of Lua is supported.
76 Future versions of ELinks will probably support Lua 5.1 too;
77 see http://bugzilla.elinks.cz/show_bug.cgi?id=742[bug 742].
83 Follow the instructions for building ELinks (it is the standard
84 `./configure; make; make install` procedure). During the configure
85 step make sure that Lua has been detected on your system.
88 Running ELinks with Lua
89 ^^^^^^^^^^^^^^^^^^^^^^^
91 Simply start ELinks as you normally would. To check you have Lua support
92 compiled in, open up the "Help | About" dialog box. It should list
93 "Scripting (Lua)" under "Features".
94 If not, make sure you do not have other copies of ELinks
95 running, or start ELinks again with the "-no-connect" option on the
100 ~~~~~~~~~~~~~~~~~~~~~
102 Out of the box, ELinks with Lua will do nothing different from regular ELinks.
103 You need to write some scripts.
108 The Lua support is based on the idea of *hooks*. A hook is a function that
109 gets called at a particular point during the execution of ELinks. To make
110 ELinks do what you want, you can add and edit such hooks.
112 The Lua support also adds an extra dialog box, which you can open while in
113 ELinks with the comma (`,`) key. Here you can enter Lua expressions for
114 evaluation, or override it to do something different.
116 And finally, you can bind keystrokes to Lua functions. These keystrokes
117 won't let you do any more than is possible with the Lua Console, but
118 they're more convenient.
120 Note that this document assumes you have some knowledge of programming in Lua.
121 For that, you should refer to the Lua reference manual
122 (http://www.lua.org/docs.html[]). In fact, the language is relatively
123 trivial, though. You could already do wonders with simply refactoring the
129 On startup, ELinks reads in two Lua scripts. Firstly, a system-wide
130 configuration file called `/etc/elinks/hooks.lua`, then a file in your home
131 directory called `~/.elinks/hooks.lua`. From these files, you can include
132 other Lua files with `dofile`, if necessary.
134 To see what kind of things you should put in here, look at
135 `contrib/lua/hooks.lua`.
140 The following hooks are available.
142 goto_url_hook (url, current_url)::
143 This hook is called when the user enters a string into the "Go to URL"
144 dialog box. It is given the string entered, and the current URL
145 (which may be `nil`). It should return a string, which is the URL
146 that ELinks should follow, or `nil` to cancel the operation.
148 follow_url_hook (url)::
149 This hook is passed the URL that ELinks is about to follow. It should
150 return a string (the URL modified or unmodified), or `nil` to stop
151 ELinks following the URL
153 pre_format_html_hook (url, html)::
154 This hook gets called just before the final time an HTML document is
155 formatted, i.e. it only gets called once, after the entire document is
156 downloaded. It will be passed the URL and HTML text as strings, and
157 should return the modified HTML text, or `nil` if there were no
160 proxy_for_hook (url)::
161 This hook is called when ELinks is about to load a resource
162 from a URL. It should return "PROXY:PORT" (e.g. "localhost:8080")
163 to use the specified proxy, "" to contact the origin server
164 directly, or `nil` to use the default proxy of the protocol.
166 lua_console_hook (string)::
167 This hook is passed the string that the user entered into the "Lua
168 Console" dialog box. It should return two values: the type of action
169 to take (`run`, `eval`, `goto-url` or `nil`), and
170 a second argument, which is the shell command to run or the Lua
171 expression to evaluate. Examples:
172 - `return "run", "someprogram"` will attempt to run the program
174 - `return "eval", "somefunction(1+2)"` will attempt to call the Lua
175 function `somefunction` with an argument, 3.
176 - `return "goto_url", "http://www.bogus.com"` will ask ELinks to visit
177 the URL "http://www.bogus.com".
178 - `return nil` will do nothing.
181 This hook is run just before ELinks quits. It is useful for cleaning
182 up things, such as temporary files you have created.
188 As well as providing hooks, ELinks provides some functions in addition to the
189 standard Lua functions.
191 NOTE: The standard Lua function `os.setlocale` affects ELinks' idea of
192 the system locale, which ELinks uses for the "System" charset, for the
193 "System" language, and for formatting dates. This may however have to
194 be changed in a future version of ELinks, in order to properly support
195 terminal-specific system locales.
198 Returns the URL of the current page being shown (in the ELinks session
199 that invoked the function).
202 Returns the URL of the currently selected link, or `nil` if none is
206 Returns the title of the current page, or `nil` if none.
208 current_document ()::
209 Returns the current document as a string, unformatted.
211 current_document_formatted ([width])::
212 Returns the current document, formatted for the specified screen
213 width. If the width is not specified, then the document is formatted
214 for the current screen width (i.e. what you see on screen). Note that
215 this function does *not* guarantee all lines will be shorter than
216 `width`, just as some lines may be wider than the screen when
217 viewing documents online.
219 pipe_read (command)::
220 Executes `command` and reads in all the data from stdout, until there
221 is no more. This is a hack, because for some reason the standard Lua
222 function `file:read` seems to crash ELinks when used in pipe-reading
226 Executes shell commands `string` without waiting for it to exit. Beware
227 that you must not read or write to stdin and stdout. And unlike the
228 standard Lua function `os.execute`, the return value is meaningless.
231 Returns a unique name for a temporary file, or `nil` if no
232 such name is available. The returned string includes the
233 directory name. Unlike the standard Lua function
234 `os.tmpname`, this one generates ELinks-related names
235 (currently with "elinks" at the beginning of the name).
237 WARNING: The `tmpname` function does not create the file and does not
238 guarantee exclusive access to it: the caller must handle the
239 possibility that another process creates the file and begins
240 using it while this function is returning. Failing to do this
241 may expose you to symlink attacks by other users. To avoid
242 the risk, use `io.tmpfile` instead; unfortunately, it does not
243 tell you the name of the file.
245 bind_key (keymap, keystroke, function)::
246 Currently, `keymap` must be the string `"main"`. Keystroke is a
247 keystroke as you would write it in the ELinks config file
248 `~/.elinks/elinks.conf`. The function `function` should take no
249 arguments, and should return the same values as `lua_console_hook`.
251 edit_bookmark_dialog (cat, name, url, function)::
252 Displays a dialog for editing a bookmark, and returns without
253 waiting for the user to close the dialog. The return value is
254 `1` if successful, `nil` if arguments are invalid, or nothing
255 at all if out of memory. The first three arguments
256 must be strings, and the user can then edit them in input
257 fields. There are also 'OK' and 'Cancel' buttons in the
258 dialog. If the user presses 'OK', ELinks calls `function`
259 with the three edited strings as arguments, and it should
260 return similar values as in `lua_console_hook`.
262 xdialog (string [, more strings...], function)::
263 Displays a generic dialog for editing multiple strings, and
264 returns without waiting for the user to close the dialog.
265 The return value is `1` if successful, `nil` if arguments are
266 invalid, or nothing at all if out of memory. All arguments
267 except the last one must be strings, and ELinks places them
268 in input fields in the dialog. There can be at most 5 such
269 strings. There are also 'OK' and 'Cancel' buttons in the
270 dialog. If the user presses 'OK', ELinks calls `function`
271 with the edited strings as arguments, and it should return
272 similar values as in `lua_console_hook`.
274 set_option (option, value)::
275 Sets an ELinks option. The first argument `option` must be
276 the name of the option as a string. ELinks then tries to
277 convert the second argument `value` to match the type of the
278 option. If successful, `set_option` returns `value`, else
281 get_option (option)::
282 Returns the value of an ELinks option. The argument `option`
283 must be the name of the option as a string. If the option
284 does not exist, `get_option` returns `nil`.
291 The name of the ELinks home directory, as a string. Typically
292 this is the .elinks subdirectory of the user's home directory.
298 There is one more little thing which Links-Lua adds, which will not be
299 described in detail here. It is the fake "user:" protocol, which can be used
300 when writing your own addons. It allows you to generate web pages containing
301 links to "user://blahblah", which can be intercepted by the `follow_url_hook`
302 (among other things) to perform unusual actions. For a concrete example, see
309 This chapter contains some example scripts that you can use. All of them come
310 from `contrib/lua/hooks.lua`. I really recommend you to see it directly
311 instead of copying code out of this document. Also, not everything in there is
314 If you would like to contribute scripts, that would be great! Please send
315 them to me at mailto:tjaden@users.sourceforge.net[]. Cliff and I plan to
316 start a script repository, provided we get some contributions. As for script
317 ideas, you'll just have to be a little creative :-)
319 Also take a look at the `contrib/lua/` directory in the ELinks distribution.
320 Note that Peter and Cliff don't maintain the Lua support intensively anymore,
321 thus it would be probably nice to Cc me (mailto:pasky@ucw.cz[]) if you want
322 to contribute some patch, so that I would be able to add it to the ELinks
326 Go to URL on steroids
327 ^^^^^^^^^^^^^^^^^^^^^
329 There are some web sites that I visit often. Bookmarks are okay, but they are
330 separate from the "Go to URL" dialog box, so I keep forgetting to use them.
331 Also, when I visit a search engine home page, all I really want to do is enter
334 The following script allows me to type certain strings into the "Go to URL"
335 dialog box, and it will convert them to the URL I actually want to visit. As
336 a bonus, it allows me perform some searches on sites like Google without
337 loading up the front page first.
339 TIP: The ``URI rewriting'' feature of ELinks handles many of the same
340 tasks as the Lua hook shown here, and you can conveniently configure
341 it via the option manager. It is not quite as versatile, though.
343 -------------------------------------------------------------------------------
344 function match (prefix, url)
345 return string.sub (url, 1, string.len (prefix)) == prefix
349 return string.gsub (str, "^%s*(.-)%s*$", "%1")
352 function plusify (str)
353 return string.gsub (str, "%s", "+")
356 function goto_url_hook (url, current_url)
357 -- Google search (e.g. ,gg unix browsers).
358 if match (",gg", url) then
359 url = plusify (strip (string.sub (url, 4)))
360 return "http://www.google.com/search?q="..url.."&btnG=Google+Search"
363 elseif match (",fm", url) then
364 url = plusify (strip (string.sub (url, 4)))
365 return "http://www.freshmeat.net/search/?q="..url
367 -- Dictionary.com search (e.g. ,dict congenial).
368 elseif match (",dict", url) then
369 url = plusify (strip (string.sub (url, 6)))
370 return "http://www.dictionary.com/cgi-bin/dict.pl?db=%2A&term="..url
372 -- RPM search (e.g. ,rpm links).
373 elseif match (",rpm", url) then
374 url = plusify (strip (string.sub (url, 5)))
375 return "http://www.rpmfind.net/linux/rpm2html/search.php?query="
376 ..url.."&submit=Search+..."
378 -- Netcraft.com search (e.g. ,whatis www.google.com).
379 elseif match (",whatis", url) then
380 url = plusify (strip (string.sub (url, 8)))
381 return "http://uptime.netcraft.com/up/graph/?host="..url
383 -- LinuxToday home page.
384 elseif match (",lt", url) then
385 return "http://linuxtoday.com/"
387 -- Weather forecast for Melbourne, Australia.
388 elseif match (",forecast", url) then
389 return "http://www.bom.gov.au/cgi-bin/wrap_fwo.pl?IDV10450.txt"
396 -------------------------------------------------------------------------------
402 By adding an extra snippet of code to the previous example, we can make ELinks
403 expand pathnames such as `~/foo/bar`
404 and `~user/zappo`, like in the shell
405 and other Unix programs.
407 -------------------------------------------------------------------------------
408 function goto_url_hook (url, current_url)
412 -- Expand ~ to home directories.
413 elseif match ("~", url) then
414 if string.sub(url, 2, 2) == "/" then -- ~/foo
415 return os.getenv ("HOME")..string.sub(url, 2)
417 return "/home/"..string.sub(url, 2)
422 -------------------------------------------------------------------------------
428 Many web pages nowadays have columns to the left and right of the text, which
429 are utterly useless. If you happen to be viewing the page in a 80x25 screen,
430 the text you want to read ends up crammed into a tiny space in the centre. We
431 use ELinks Lua support to manipulate the HTML before it reaches the parser.
437 NOTE: This recipe is out of date for the web site.
439 Linux Today has two problems when viewed in ELinks: the useless columns on the
440 left and the right and all the text appears in cyan. Here is a quick recipe
443 -------------------------------------------------------------------------------
444 -- Plain string.find (no metacharacters)
445 function sstrfind (s, pattern)
446 return string.find (s, pattern, 1, true)
449 function pre_format_html_hook (url, html)
450 -- Strip the left and right columns from Linux Today pages
451 -- and change the font colour to white.
452 if sstrfind (url, "linuxtoday.com") then
453 if sstrfind (url, "news_story") then
454 html = string.gsub (html, '<TABLE CELLSPACING="0".-</TABLE>', '', 1)
455 html = string.gsub (html, '<TR BGCOLOR="#FFF.-</TR></TABLE>', '', 1)
457 html = string.gsub (html, 'WIDTH="120">\n<TR.+</TABLE></TD>', '>', 1)
459 html = string.gsub (html, '<A HREF="http://www.internet.com.-</A>', '')
460 html = string.gsub (html, "<IFRAME.-</IFRAME>", "")
461 -- emphasis in text is lost
462 return string.gsub (html, 'text="#002244"', 'text="#001133"', 1)
467 -------------------------------------------------------------------------------
472 NOTE: This recipe is out of date for the web site.
474 Here is a simpler example, for link:http://www.linuxgames.com/[].
476 -------------------------------------------------------------------------------
477 function pre_format_html_hook (url, html)
481 elseif string.find (url, "linuxgames.com", 1, true) then
482 return string.gsub (html, "<CENTER>.-</center>", "", 1)
486 -------------------------------------------------------------------------------
489 Reading gzipped files
490 ^^^^^^^^^^^^^^^^^^^^^
492 NOTE: ELinks already supports gzipped files natively.
494 Sometimes documents come gzipped in order to save space, but then you need to
495 uncompress them to read them with ELinks. Here is a recipe to handle gzipped
496 files on a Unix system.
498 WARNING: This recipe opens a temporary file insecurely.
500 -------------------------------------------------------------------------------
501 function pre_format_html_hook (url, html)
505 -- Handle gzip'd files within reasonable size.
506 if string.find (url, "%.gz$") and string.len (html) < 65536 then
507 local name = tmpname ()
508 local file = io.open (name, "wb")
511 html = pipe_read ("(gzip -dc "..name.." || cat "..name..") 2>/dev/null")
518 -------------------------------------------------------------------------------
524 Printing a web page with ELinks usually involves quite a few steps: Save the
525 current document onto disk. Run it through ELinks on the command-line (so it
526 fits into 80 columns) to generate a plain text version. Remove the 80th
527 column from the text version, as it will make printers wrap down to the next
528 line. Finally, run the processed file through `lpr', then delete it.
530 The following functions allow you to print web pages directly from ELinks,
531 using `lpr' or `enscript'. Type `lpr()` or `enscript()` in the Lua Console to
532 run them. (In the `hooks.lua`, I have also made it so you can just type `lpr`
535 NOTE: The `io.popen` function is not available on all platforms.
537 -------------------------------------------------------------------------------
538 function pipe_formatted_to (program)
539 local lp, errmsg = io.popen (program, "w")
543 lp:write (current_document_formatted (79))
548 -- Send the current document to `lpr'.
550 pipe_formatted_to ("lpr")
553 -- Send the current document to `enscript'.
555 pipe_formatted_to ("enscript -fCourier8")
557 -------------------------------------------------------------------------------
560 Deferring to Netscape
561 ^^^^^^^^^^^^^^^^^^^^^
563 If you come across a brain-dead web page that is totally unreadable with
564 ELinks, you'd probably want to open it with a graphical browser. The
565 following function opens the current document in Netscape.
567 TIP: You can also use the built-in ``URI passing'' feature for this.
569 -------------------------------------------------------------------------------
570 -- When starting Netscape: Set to `nil' if you do not want
571 -- to open a new window for each document.
572 netscape_new_window = 1
574 -- Open current document in Netscape.
576 local new = netscape_new_window and ",new_window" or ""
577 execute ("( netscape -remote 'openURL("..current_url ()..new..")'"
578 .." || netscape '"..current_url ().."' ) 2>/dev/null &")
580 -------------------------------------------------------------------------------
583 Alternative bookmark system
584 ^^^^^^^^^^^^^^^^^^^^^^^^^^^
586 Many people would like to have a bookmark system with categories (note that
587 ELinks already supports that, marketing name Hierarchical bookmarks), and also
588 to be able to view them and search for them in an HTML page. I have written
589 an alternative bookmark system (for ELinks), which some people may like better
590 than the standard bookmark system.
596 - The Lua interface needs to be redesigned to provide more flexible, coherent
597 and usable interface to the scripts.
599 - Cliff Cunnington had a neat idea of clipping text that you see in web pages
600 (you enter a regexp that will match the start and end of the text you want
601 to clip), and saving the text to disk, along with the URL and timestamp.
602 This would help if you find that you can't ever remember where you had seen
603 a piece of text, or if you want to keep a piece of information but don't
604 need to save the entire page.
606 - People who use download management programs could write a function to send
607 the current link to their favourite downloading program.
609 - If you wrote a small C program to put text into the X11 selection
610 clipboard, you could pass the current link or URL to that program, to make
611 it easier to paste URLs into other windows. It might be possible to do the
612 same with GPM, or the KDE/GNOME equivalents.
614 - Send the current page to Babelfish for translation.
616 - Look for stupid JavaScript URLs and convert them to something usable.
618 - More things are possible, I'm sure. If you have an idea that requires
619 another hook or function, contact me (Peter Wang) and I'll see what I can