Welcome, mere mortal, to realm of evil unindented code, heaps of
one^Wtwo-letter variables, seas of gotos accompanied with 30-letters labels in
czech language with absoltely no mean, welcome to the realm of elinks code!

[I'm going to extend this file slowly, basing on daily practice ;]

Don't take this file as a law. I may or may not be right in these issues.

Motto: I didn't expect someone to look at the source, so it's unreadable.
       --Mikulas


1, Language files
~~~~~~~~~~~~~~~~~

Each UI output should use language files in order to be able to translate the
messages to desired language. Those language files reside in intl/ subdir, and
each time when you add/remove something from there, thou shall call ./synclang
script, so that the change will propagate from english.lng to all other
language files and lang_defs.h && language.inc files will be generated
automagically. Thou shall NEVER modify those two files manually, or thou wilt
be cursed for the rest of your life and all patches of thee will be trampled
in.


2, Links philosophy
~~~~~~~~~~~~~~~~~~~

Links is based on the philosophy of asynchronity. That means, you pass callback
to each function, and when the requested task will be finished sometime in the
unclear future, the callback will be called in order to notify you that you can
go on.

When you use multiple links instances at once, normally only one of them is a
'master', and the rest of them are just kinda dumb terminals. So if you want to
test some new feature, you have to start links with '-no-connect' parameter,
otherwise it will just connect to actually running links and you won't see your
new great feature you just added in such a big pain.

There are two basic structures in links. Connection and session - their names
should be self-descriptive. Only note that connection may or may not have
session attached. I.e. if you will type some URL, start loading it and then
press ACT_BACK (KEY_LEFT), connection will lose its session. So you should
never prepend that it has one.

UI just plainly needs rewrite as it's extremely ugly ;-). Cut'n'paste from some
existing code and modify appropriately, if you want one :).

Some common used functions (i.e. malloc() or free()) have their own clones
inside links and you should use them (i.e. mem_alloc() or mem_free()) instead.
See links.h for list of them ;-).

If you want to have anything else documented here, write description and send
me a patch.


2.1, HTML parser
~~~~~~~~~~~~~~~~

I found the following in mailing list archive - Mikulas wrote it:

The entry of parser is parse_html. This function gets html to parse (pointers
html and eof) and functions it should call when it wants to produce output
(put_chars, line_break, init, special). f is pointer that is passed to that
functions, head is HTTP header (for refresh). The function uses stack (struct
list_head html_stack; it is set up by the caller of parse_html), places
formatting info on it, and calls output functions. put_chars to write text,
line_break for new line, init (currently unused), special (tags, forms,
etc...). These functions read the parameters from stack (struct html_element)
and do actual rendering.  (some times ago, some people wanted to use the parser
for graphics browser, so I wrote it so that it could produce graphics output
just by replacing the output functions)

Html renderer is in html_r.c. html_interpret formats the whole screen. It calls
cached_format_html which formats one document (frame) or returns directly
formatted document from cache. cached_format_html then calls format_html, which
sets up rendering and calls format_html_part.  format_html_part is used for
formatting the whole document or formatting parts of tables. It calls
parse_html.

The rendering functions put_chars_conv, line_break, html_special receive struct
part * from the parser. They render the text or calculate size of text (in case
of tables). struct part has pointer to struct f_data - a stucture that holds
actually formatted data. If the pointer is NULL, rendering functions only
calculate size.


2.2, Lua support
~~~~~~~~~~~~~~~~

Peter Wang wrote this on the mailing list:

The parts of the Links-Lua and ELinks code that related to Lua are mostly
wrapped inside #ifdef HAVE_LUA ...  #endif conditions, so they're easy to find.
And also lua.c.

In certain situations, I put some hooks into the C code.  These prepare the C
code to run some Lua code, run it, then translate the return values to
something meaningful to the C code.  They are really simple, but I had to put
in some cruft.

For example, the code implementing the Go To URL dialog box hook :-

  void goto_url_with_hook(struct session *ses, unsigned char *url)
  {
  #ifndef HAVE_LUA
          goto_url(ses, url);
  #else
          lua_State *L = lua_state;
          int err;

[1]       lua_getglobal(L, "goto_url_hook");
          if (lua_isnil(L, -1)) {
                  lua_pop(L, 1);
                  goto_url(ses, url);
                  return;
          }

[2]       lua_pushstring(L, url);
          if (list_empty(ses->history)) lua_pushnil(L);
          else lua_pushstring(L, cur_loc(ses)->vs.url);

[3]       if (prepare_lua(ses)) return;
[4]       err = lua_call(L, 2, 1);
[5]       finish_lua();
          if (err) return;

[6]       if (lua_isstring(L, -1)) goto_url(ses, (unsigned char *) lua_tostring(L, -1));
          else if (!lua_isnil(L, -1)) alert_lua_error("goto_url_hook must return a string or nil");
          lua_pop(L, 1);
  #endif
  }

Quick description of what it does:

[1] Get the value named `goto_url_hook' from the Lua state.  If it doesn't
    exist, the user hasn't installed a hook so continue the normal way.

[2] Push the arguments onto the Lua stack for the function call.
    `goto_url_hook' takes two parameters, the new URL and the current URL, so
    we push them on.

[3] Some stuff to prepare for running Lua.  This is in case for example the Lua
    function contains bugs, we want to trap some of that.  It's supposed to
    enable a mechanism where ^C will break out of infinite loops and such, but
    that's not working so good.

[4] Call the Lua function, ie. goto_url_hook

[5] Disable the error trapping stuff.

[6] Look at the return value of goto_url_hook and do something appropriate.

[If you want to know the lua_* functions, you're gonna have to read the Lua API
manual]


That's basically all the Links-Lua stuff is, a bunch of hooks that pass
arguments to some arbitrary Lua code, then read in return values and interpret
it.  But it works because Lua is a programming language, and that enables you
to do what you would from C, as long as you stick to the C<->Lua interface.

The code to allow binding Lua code to a keypress is slightly different to the
hooks, but not much.  Instead of branching off to some C code when a key is
pressed, we branch off to some Lua code.



3, Coding style
~~~~~~~~~~~~~~~

Mikulas once told that 'it was hard to code, so it should be hard to read' -
and he drove by this when he has been writing links. However, we do NOT drive
by this rule anymore ;-). I strongly welcome cleanup patches and you have 99%
proximity they will be applied, if they will be correct.

Variable names should be descriptive. Well, in worst case I will accept 'i' for
index, but if possible don't do even this :). You should try to declare them on
the lowest level possible, so their declaration and initialization will be near
their usage and you will also prevent reuse of one variable for two completely
diferent things. Yes, that's another thing you shouldn't do. If reasonable,
initialize variables during their declaration and don't group too much
variables in one line. ALWAYS make blank line after declaration part.

And don't specify variable names in prototypes - just types of variables, who's
interested in the function, he'll probably want to look at the code anyway. And
prefix all local functions with 'static'.

Indent shift width is 8, that means one tab. I'm using to prefer 2, but links
is actually written using 8, and it doesn't appear to be wise to change this.
{ should be in same line as condition near for, if, while, switch etc, but on
separate line near function header in function definition.

Please _USE_ one tab instead of 8 spaces! It's making things easier.

Always make spaces around '=', after ',', and - if reasonable - around other
operators as well. You shouldn't make assigments in conditionals, so do:

foo = mem_alloc(1234);
if (!foo) panic("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");

instead of

if (!(foo = mem_alloc(1234))) panic(aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");

(note that thou shalt ALWAYS test for success of everything - we are supposed
to handle even our own death ellegantly)

Use blank lines frequently to separate chunks of code. And use magic "/*" and
"*/" to give others an idea about what it does and about possible pitfalls.

Keep in mind that if there's an object and verb in the comment (and it is, in
most cases), it is a sentence, so it has to start with capital letter and end
with a fullstop ;-). If you want to share your opinion or investigations, you
should sign yourself, i.e. /* TODO: Move this far away! --pasky */

All three following styles of comments are acceptable:

/*
 * Bla bla bla.
 * blabla?
 */

/* Bla bla bla.
 * blabla?
 */

/* Bla bla bla.
 * blabla? */

The best one usually depends on the actual context.

If you describe a structure, make either:

        char *name; /* name of the tag */
        void (*func)(unsigned char *); /* function hopefully handling the
                                        * content of the tag */
        int linebreak; /* need for break of a line? */

or

        /* Name of the tag */
        char *name;
        /* Function hopefully handling the content of the tag */
        void (*func)(unsigned char *);
        /* Do we need to break a line? */
        int linebreak;

Same if you comment the code. If the comment is /* then do it now! */, place it
on the same line as the command, if it's
/* We have to parse this crap now, as it is going to be freed in free_crap() */
place it on the preceding line.


4, Patches
~~~~~~~~~~

Please sent unified patches only. Recommended way is:

cp -a elinks/ elinks+mysuperfeature/
cd elinks+mysuperfeature/
*clap clap* *clickety clickey* *make* *./links -no-connect* *goto clap*
cd ..
diff -ru elinks/ elinks+mysuperfeature/ >elinks-mysuperfeature.patch

please manually remove any bloat like changes in ./configure, whistespace
changes etc ;-).

I accept also output from "cvs diff -u" :). Statement about bloat removing
applies.


Happy hacking!

$Id: HACKING,v 1.12 2002/03/16 21:02:48 pasky Exp $
