diff options
Diffstat (limited to 'Util')
-rw-r--r-- | Util/zsh-development-guide | 291 |
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 ------------- |