Actions implemented as fungw functions
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

1. Intro

All pcb-rnd actions are fungw functions by now. This helps the code to do
all the data type conversions easier, save a few conversions where we
don't need conversions. More importantly, this makes a more seamless
integration between user scripts and C code, whether scripts are calling
C actions or the user is executing script defined actions.

To stay compatible with the old action mechanism, the current version
applies the following restrictions:

- we have only one large fungw object (pcb_fgw_obj) that hosts all
  functions; this emulates the old central hash of functions

- before a function enters the object or a function is looked up in the
  object, the function name is converted to lowercase; this emulates the
  case intensity of the old action system, which is still essential for
  the user interface (GUI or CLI command entry and menu).

- temporarily, for the transition, there are two helper macros
  PCB_OLD_ACT_BEGIN and PCB_OLD_ACT_END that can wrap old function
  code, converting all arguments to a char **argv. Such old actions
  should return non-zero on error, while the value of ores will always be
  FGW_INT/0. New action code shall NOT use the compatibility API.

- a single action call will result in a single fungw function call; that
  is, the fungw function that serves the action is free to convert any
  of the arguments.

2. converting arguments

Arguments are normally converted using PCB_ACT_CONVARG(); the last
argument is a statement that can make the value assignment to a local
variable if the conversion was succesful. For example fetching the 2nd
argument into a local coord:

{
	pcb_coord_t c;
	PCB_ACT_CONVARG(2, FGW_COORD, ActioName, c = fgw_coord(&argv[2]));
}

If conversion fails or the argument is not available, the macro returns
from the function with error. Anoter variant of the macro called
PCB_ACT_MAY_CONVARG() omits the error-return part, allowing the code
to have optional arguments.

When the coordinate conversion needs to keep extra info, e.g. whether
the coordinate was specified in absolute or relative, the special type
fgw_coords_t shall be used:

{
	fgw_coords_t *crd;
	PCB_ACT_CONVARG(1, FGW_COORDS, d1, crd = fgw_coords(&argv[1]));
}


3. returning

An action function has two return values: the C function return value
and the "res" argument. If the action could not start executing code
because of a calling error, e.g. there are not enough arguments or
arguments can not be converted or the function is invoked out-of-context,
it should retrun a fungw error using the C return value. This indicates
that performing the call itself failed.

Else, if actual action code runs, the C return value should be 0 and the
res argument should be loaded with the return value of the action. This
indicates that the call took place, the action was executed and the conclusion
is stored in res. The semantic of the result is action-specifiec. The common
convention is returning int, 0 meaning success. The shortnad for this:

{
	PCB_ACT_IRES(0);
	return 0;
}

PCB_ACT_IRES() loads res with type and value. There are other similar,
type-specific macros available.

4. multi-call

Currently pcb-rnd does not depend on fungw's multi-call, thus actions are
free to convert their arguments - the same arguments will not be reused in
other actions.

5. calling an action from another action

There are two common cases: wrapping/dispatching and invoking.

In the wrap/dispatch setup an outer action is called that does not alter
the arguments (not even converting them!), but has to call another action
with exactly the same arguments. This can be done by using the macro
PCB_ACT_CALL_C().

When an action code needs to invoke another action, there are three ways
doing it.

5.1. string call

The slow, string parsing way using pcb_actionl() or the more
efficient direct call.


5.2. direct C call

Direct C function call is possible only if both the caller and callee
actions are in core, or are in the same plugin. Before the direct call
the caller needs to set up a fungw argv array and after the call the
array members need to be free'd using fgw_argv_free() to avoid
leaking argument memory. This needs to be done even if the caller did
not allocate any memory for argv members - the callee can allocate memory
by converting arguments.

5.3. indirect C call

Call pcb_find_action() to get an fgw_func_t * to the action - this holds
the C function pointer. Set up argv, make sure argv[0] is set up to be
the target function:

  fgw_func_t *fn = pcb_find_action(...);
  argv[0].type = FGW_FUNC;
  argv[0].val.func = fn;

Call pcb_actionv_() with fn and the arguments. Always call fgw_argv_free()
afterward. Also call fgw_arg_free() on the res.

