about summary refs log tree commit diff
path: root/Util
diff options
context:
space:
mode:
Diffstat (limited to 'Util')
-rw-r--r--Util/zsh-development-guide291
1 files changed, 291 insertions, 0 deletions
diff --git a/Util/zsh-development-guide b/Util/zsh-development-guide
index d574d8af0..73ac120ab 100644
--- a/Util/zsh-development-guide
+++ b/Util/zsh-development-guide
@@ -112,6 +112,297 @@ C coding style
   groups of statements in the interests of clarity.  There should never
   be two consecutive blank lines.
 
+Modules
+-------
+
+Modules are described by a file named `foo.mdd' for a module
+`foo'. This file is actually a shell script that will sourced when zsh 
+is build. To describe the module it can/should set the following shell 
+variables:
+
+  - moddeps       modules on which this module depends (default none)
+  - nozshdep      non-empty indicates no dependence on the `zsh' pseudo-module
+  - alwayslink    if non-empty, always link the module into the executable
+  - autobins      builtins defined by the module, for autoloading
+  - objects       .o files making up this module (*must* be defined)
+  - proto         .pro files for this module (default generated from $objects)
+  - headers       extra headers for this module (default none)
+  - hdrdeps       extra headers on which the .mdh depends (default none)
+  - otherincs     extra headers that are included indirectly (default none)
+
+Be sure to put the values in quotes. For further enlightenment have a
+look at the `mkmakemod.sh' script in the Src directory of the
+distribution.
+
+Modules have to define four functions which will automatically called
+by the zsh core. The first one, named `setup_foo' for a module named
+`foo', should set up any data needed in the module, at least any data
+other modules may be interested in. The second one, named `boot_foo',
+should register all builtins, conditional codes, and function wrappers
+(i.e. anything that will be visible to the user) and will be called
+after the `setup'-function. 
+The third one, named `cleanup_foo' for module `foo' is called when the
+user tries to unload a module and should de-register the builtins
+etc. The last function, `finish_foo' is called when the module is
+actually unloaded and should finalize all the data initialized in the 
+`setup'-function. Since the last two functions are only executed when
+the module is used as an dynamically loaded module you can surround
+it with `#ifdef MODULE' and `#endif'.
+In short, the `cleanup'-function should undo what the `boot'-function
+did, and the `finish'-function should undo what the `setup'-function
+did.
+All of these functions should return zero if they succeeded and
+non-zero otherwise.
+
+Builtins are described in a table, for example:
+
+  static struct builtin bintab[] = {
+    BUILTIN("example", 0, bin_example, 0, -1, 0, "flags", NULL),
+  };
+
+Here `BUILTIN(...)' is a macro that simplifies the description. Its
+arguments are:
+  - the name of the builtin as a string
+  - optional flags (see BINF_* in zsh.h)
+  - the C-function implementing the builtin
+  - the minimum number of arguments the builtin needs
+  - the maximum number of arguments the builtin can handle or -1 if
+    the builtin can get any number of arguments
+  - an integer that is passed to the handler function and can be used
+    to distinguish builtins if the same C-function is used to
+    implement multiple builtins
+  - the options the builtin accepts, given as a string containing the
+    option characters (the above example makes the builtin accept the
+    options `f', `l', `a', `g', and `s')
+  - and finally a optional string containing option characters that
+    will always be reported as set when calling the C-function (this,
+    too, can be used when using one C-function to implement multiple
+    builtins)
+
+The definition of the handler function looks like:
+
+  /**/
+  static int
+  bin_example(char *nam, char **args, char *ops, int func)
+  {
+    ...
+  }
+
+The special comment /**/ is used by the zsh Makefile to generate the
+`*.pro' files. The arguments of the function are the number under
+which this function was invoked (the name of the builtin, but for
+functions that implement more than one builtin this information is
+needed). The second argument is the array of arguments *excluding* the 
+options that were defined in the struct and which are handled by the
+calling code. These options are given as the third argument. It is an
+array of 256 characters in which the n'th element is non-zero if the
+option with ASCII-value n was set (i.e. you can easily test if an
+option was used by `if (ops['f'])' etc.). The last argument is the
+integer value from the table (the sixth argument to `BUILTIN(...)').
+The integer return value by the function is the value returned by the
+builtin in shell level.
+
+To register builtins in zsh and thereby making them visible to the
+user the function `addbuiltins()' is used:
+
+  /**/
+  int
+  boot_example(Module m)
+  {
+    int ret;
+
+    ret = addbuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab));
+    ...
+  }
+
+The arguments are the name of the module (taken from the argument in
+the example), the table of definitions and the number of entries in
+this table.
+The return value is 1 if everything went fine, 2 if at least one
+builtin couldn't be defined, and 0 if none of the builtin could be
+defined.
+
+To de-register builtins use the function `deletebuiltins()':
+
+  /**/
+  int
+  cleanup_example(Module m)
+  {
+    deletebuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab));
+    ...
+  }
+
+The arguments and the return value are the same as for `addbuiltins()'
+
+The definition of condition codes in modules is equally simple. First
+we need a table with the descriptions:
+
+  static struct conddef cotab[] = {
+    CONDDEF("len", 0, cond_p_len, 1, 2, 0),
+    CONDDEF("ex", CONDF_INFIX, cond_i_ex, 0, 0, 0),
+  };
+
+Again a macro is used, with the following arguments:
+
+  - the name of the condition code without the leading hyphen
+    (i.e. the example makes the condition codes `-len' and `-ex'
+    usable in `[[...]]' constructs)
+  - an optional flag which for now can only be CONDF_INFIX; if this is 
+    given, an infix operator is created (i.e. the above makes
+    `[[ -len str ]]' and `[[ s1 -ex s2 ]]' available)
+  - the C-function implementing the conditional
+  - for non-infix condition codes the next two arguments give the
+    minimum and maximum number of string the conditional can handle
+    (i.e. `-len' can get one or two strings); as with builtins giving
+    -1 as the maximum number means that the conditional accepts any
+    number of strings
+  - finally as the last argument an integer that is passed to the
+    handler function that can be used to distinguish different
+    condition codes if the same C-function implements more than one of 
+    them
+
+The definition for the function looks like:
+
+  /**/
+  static int
+  cond_p_len(char **a, int id)
+  {
+    ...
+  }
+
+The first argument is an array containing the strings (NULL-terminated
+like the array of arguments for builtins), the second argument is the
+integer value stored in the table (the last argument to `CONDDEF(...)').
+The value returned by the function should be non-zero if the condition 
+is true and zero otherwise.
+
+Note that no preprocessing is done on the strings. This means that
+no substitutions are performed on them and that they will be
+tokenized. There are three helper functions available:
+
+  - char *cond_str(args, num)
+    The first argument is the array of strings the handler function
+    got as an argument and the second one is an index into this array.
+    The return value is the num'th string from the array with
+    substitutions performed and untokenized.
+  - long cond_val(args, num)
+    The arguments are the same as for cond_str(). The return value is
+    the result of the mathematical evaluation of the num'th string
+    form the array.
+  - int cond_match(args, num, str)
+    Again, the first two arguments are the same as for the other
+    functions. The third argument is any string. The result of the
+    function is non-zero if the the num'th string from the array taken 
+    as a glob pattern matches the given string.
+
+Registering and de-resgitering condition codes with the shell is
+almost exactly the same as for builtins, using the functions
+`addconddefs()' and `deleteconddefs()' instead:
+
+  /**/
+  int
+  boot_example(Module m)
+  {
+    int ret;
+
+    ret = addconddefs(m->nam, cotab, sizeof(cotab)/sizeof(*cotab));
+    ...
+  }
+
+  /**/
+  int
+  cleanup_example(Module m)
+  {
+    deleteconddefs(m->nam, cotab, sizeof(cotab)/sizeof(*cotab));
+    ...
+  }
+
+Arguments and return values are the same as for the functions for
+builtins.
+
+Finally, modules can define wrapper functions. These functions are
+called whenever a shell function is to be executed.
+
+The definition is simple:
+
+  static struct funcwrap wrapper[] = {
+    WRAPDEF(ex_wrapper),
+  };
+
+The macro `WRAPDEF(...)' gets the C-function as its only argument.
+This function should be defined like:
+
+  /**/
+  static int
+  ex_wrapper(List list, FuncWrap w, char *name)
+  {
+    ...
+    runshfunc(list, w, name);
+    ...
+    return 0;
+  }
+
+The first two arguments should only be used to pass them to
+`runshfunc()' which will execute the shell function. The last argument 
+is the name of the function to be executed. The arguments passed to
+the function can be accessed vie the global variable `pparams' (a
+NULL-terminated array of strings).
+The return value of the wrapper function should be zero if it calls
+`runshfunc()' itself and non-zero otherwise. This can be used for
+wrapper functions that only need to run under certain conditions or
+that don't need to clean anything up after the shell function has
+finished:
+
+  /**/
+  static int
+  ex_wrapper(List list, FuncWrap w, char *name)
+  {
+    if (wrapper_need_to_run) {
+      ...
+      runshfunc(list, w, name);
+      ...
+      return 0;
+    }
+    return 1;
+  }
+
+Inside these wrapper functions the global variable `sfcontext' will be 
+set to a vlue indicating the circumstances under which the shell
+function was called. It can have any of the following values:
+
+  - SFC_DIRECT:   the function was invoked directly by the user
+  - SFC_SIGNAL:   the function was invoked as a signal handler
+  - SFC_HOOK:     the function was automatically invoked as one of the
+                  special functions known by the shell (like `chpwd')
+  - SFC_WIDGET:   the function was called from the zsh line editor as a
+                  user-defined widget
+  - SFC_COMPLETE: the function was called from the completion code
+                  (e.g. with `compctl -K func')
+
+If a module invokes a shell function (e.g. as a hook function), the
+value of this variable should only be changed temporarily and restored
+to its previous value after the shell function has finished.
+
+There is a problem when the user tries to unload a module that has
+defined wrappers from a shell function. In this case the module can't
+be unloaded immediately since the wrapper function is still on the
+call stack. The zsh code delays unloading modules until all wrappers
+from them have finished. To hide this from the user, the module's
+cleanup function is run immediatly so that all builtins, condition
+codes, and wrapper function defined by the module are
+de-registered. But if there is some module-global state that has to be 
+finalized (e.g. some memory that has to be freed) and that is used by
+the wrapper functions finalizing this data in the cleanup function
+won't work.
+This is why ther are two functions each for the initialization and
+finalization of modules. The `boot'- and `cleanup'-functions are run
+whenever the user calls `zmodload' or `zmodload -u' and should only
+register or de-register the module's interface that is visible to the
+user. Anything else should be done in the `setup'- and
+`finish'-functions. Otherwise modules that other modules depend upon
+may destroy their state too early and wrapper functions in the latter
+modules may stop working since the state they use is already destroyed.
+
 Documentation
 -------------