diff options
Diffstat (limited to 'Etc/zsh-development-guide')
-rw-r--r-- | Etc/zsh-development-guide | 673 |
1 files changed, 673 insertions, 0 deletions
diff --git a/Etc/zsh-development-guide b/Etc/zsh-development-guide new file mode 100644 index 000000000..4ec4ff079 --- /dev/null +++ b/Etc/zsh-development-guide @@ -0,0 +1,673 @@ +------------------------------ +GUIDELINES FOR ZSH DEVELOPMENT +------------------------------ + +Zsh is currently developed and maintained by the Zsh Development Group. +This development takes place by mailing list. Check the META-FAQ for the +various zsh mailing lists and how to subscribe to them. The development +is very open and anyone is welcomed and encouraged to join and contribute. +Because zsh is a very large package whose development can sometimes +be very rapid, I kindly ask that people observe a few guidelines when +contributing patches and feedback to the mailing list. These guidelines +are very simple and hopefully should make for a more orderly development +of zsh. + +Patches +------- + +* Send all patches to the mailing list rather than directly to me. + +* Send only context diffs "diff -c oldfile newfile" or unified diffs + "diff -u oldfile newfile". They are much easier to read and + understand while also allowing the patch program to patch more + intelligently. Please make sure the filenames in the diff header + are relative to the top-level directory of the zsh distribution; for + example, it should say "Src/init.c" rather than "init.c" or + "zsh/Src/init.c". + +* Please put only one bug fix or feature enhancement in a single patch and + only one patch per mail message. This helps me to multiplex the many + (possibly conflicting) patches that I receive for zsh. You shouldn't + needlessly split patches, but send them in the smallest LOGICAL unit. + +* If a patch depends on other patches, then please say so. Also please + mention what version of zsh this patch is for. + +* Please test your patch and make sure it applies cleanly. It takes + considerably more time to manually merge a patch into the baseline code. + +* There is now a zsh patch archive. To have your patches appear in the + archive, send them to the mailing list with a Subject: line starting + with "PATCH:". + +C coding style +-------------- + +* The primary language is ANSI C as defined by the 1989 standard, but the + code should always be compatible with late K&R era compilers ("The C + Programming Language" 1st edition, plus "void" and "enum"). There are + many hacks to avoid the need to actually restrict the code to K&R C -- + check out the configure tests -- but always bear the compatibility + requirements in mind. In particular, preprocessing directives must + have the "#" unindented, and string pasting is not available. + +* Conversely, there are preprocessor macros to provide safe access to some + language features not present in pure ANSI C, such as variable-length + arrays. Always use the macros if you want to use these facilities. + +* Avoid writing code that generates warnings under gcc with the default + options set by the configure script. For example, write + "if ((foo = bar))" rather than "if (foo = bar)". + +* Please try not using lines longer than 79 characters. + +* The indent/brace style is Kernighan and Ritchie with 4 characters + indentations (with leading tab characters replacing sequences of + 8 spaces). This means that the opening brace is the last character + in the line of the if/while/for/do statement and the closing brace + has its own line: + + if (foo) { + do that + } + +* Put only one simple statement on a line. The body of an if/while/for/do + statement has its own line with 4 characters indentation even if there + are no braces. + +* Do not use space between the function name and the opening parenthesis. + Use space after if/for/while. Use space after type casts. + +* Do not use (unsigned char) casts since some compilers do not handle + them properly. Use the provided STOUC(X) macro instead. + +* If you use emacs 19.30 or newer you can put the following line to your + ~/.emacs file to make these formatting rules the default: + + (add-hook 'c-mode-common-hook (function (lambda () (c-set-style "BSD")))) + +* Function declarations must look like this: + + /**/ + int + foo(char *s, char **p) + { + function body + } + + There must be an empty line, a line with "/**/", a line with the + type of the function, and finally the name of the function with typed + arguments. These lines must not be indented. The script generating + function prototypes and the ansi2knr program depend on this format. + If the function is not used outside the file it is defined in, it + should be declared "static"; this keyword goes on the type line, + before the return type. + +* Global variable declarations must similarly be preceded by a + line containing only "/**/", for the prototype generation script. + The declaration itself should be all on one line (except for multi-line + initialisers). + +* Leave a blank line between the declarations and statements in a compound + statement, if both are present. Use blank lines elsewhere to separate + 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 + - autoinfixconds infix condition codes defined by the module, for + autoloading (without the leading `-') + - autoprefixconds like autoinfixconds, but for prefix condition codes + - autoparams parameters 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 be called automatically +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, raw) + 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. If the last argument is zero, the string + will also be 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. + +For defining parameters, a module can call `createparam()' directly or +use a table to describe them, e.g.: + + static struct paramdef patab[] = { + PARAMDEF("foo", PM_INTEGER, NULL, get_foo, set_foo, unset_foo), + INTPARAMDEF("exint", &intparam), + STRPARAMDEF("exstr", &strparam), + ARRPARAMDEF("exarr", &arrparam), + }; + +There are four macros used: + + - PARAMDEF() gets as arguments: + - the name of the parameter + - the parameter flags to set for it (from the PM_* flags defined + in zsh.h) + - optionally a pointer to a variable holding the value of the + parameter + - three functions that will be used to get the value of the + parameter, store a value in the parameter, and unset the + parameter + - the other macros provide simple ways to define the most common + types of parameters; they get the name of the parameter and a + pointer to a variable holding the value as arguments; they are + used to define integer-, scalar-, and array-parameters, so the + variables whose addresses are given should be of type `long', + `char *', and `char **', respectively + +For a description of how to write functions for getting or setting the +value of parameters, or how to write a function to unset a parameter, +see the description of the following functions in the `params.c' file: + + - `intvargetfn()' and `intvarsetfn()' for integer parameters + - `strvargetfn()' and `strvarsetfn()' for scalar parameters + - `arrvargetfn()' and `arrvarsetfn()' for array parameters + - `stdunsetfn()' for unsetting parameters + +Note that if one defines parameters using the last two macros (for +scalars and arrays), the variable holding the value should be +initialized to either `NULL' or to a a piece of memory created with +`zalloc()'. But this memory should *not* be freed in the +finish-function of the module because that will be taken care of by +the `deleteparamdefs()' function described below. + +To register the parameters in the zsh core, the function +`addparamdefs()' is called as in: + + /**/ + int + boot_example(Module m) + { + int ret; + + ret = addparamdefs(m->nam, patab, sizeof(patab)/sizeof(*patab)) + ... + } + +The arguments and the return value are as for the functions used to +add builtins and condition codes and like these, it should be called +in the boot-function of the module. To remove the parameters defined, +the function `deleteparamdefs()' should be called, again with the same +arguments and the same return value as for the functions to remove +builtins and condition codes: + + /**/ + int + cleanup_example(Module m) + { + deleteparamdefs(m->nam, patab, sizeof(patab)/sizeof(*patab)); + ... + } + +Modules can also define function hooks. Other modules can then add +functions to these hooks to make the first module call these functions +instead of the default. + +Again, an array is used to define hooks: + + static struct hookdef foohooks[] = { + HOOKDEF("foo", foofunc, 0), + }; + +The first argument of the macro is the name of the hook. This name +is used whenever the hook is used. The second argument is the default +function for the hook or NULL if no default function exists. The +last argument is used to define flags for the hook. Currently only one +such flag is defined: `HOOKF_ALL'. If this flag is given and more than +one function was added to the hook, all functions will be called +(including the default function). Otherwise only the last function +added will be called. + +The functions that can be used as default functions or that can be +added to a hook have to be defined like: + + /**/ + static int + foofunc(Hookdef h, void *data) + { + ... + } + +The first argument is a pointer to the struct defining the hook. The +second argument is an arbitrary pointer that is given to the function +used to invoke hooks (see below). + +The functions to register and de-register hooks look like those for +the other things that can be defined by modules: + + /**/ + int + boot_foo(Module m) + { + int ret; + + ret = addhookdefs(m->nam, foohooks, sizeof(foohooks)/sizeof(*foohooks)) + ... + } + ... + /**/ + int + cleanup_foo(Module m) + { + deletehookdefs(m->nam, foohooks, sizeof(foohooks)/sizeof(*foohooks)); + ... + } + +Modules that define hooks can invoke the function(s) registered for +them by calling the function `runhook(name, data)'. The first argument +is the name of the hook and the second one is the pointer given to the +hook functions as their second argument. Hooks that have the `HOOKF_ALL' +flag call all function defined for them until one returns non-zero. +The return value of `runhook()' is the return value of the last hook +function called or zero if none was called. + +To add a function to a hook, the function `addhookfunc(name, func)' is +called with the name of the hook and a hook function as arguments. +Deleting them is done by calling `deletehookfunc(name, func)' with the +same arguments as for the corresponding call to `addhookfunc()'. + +Alternative forms of the last three function are provided for hooks +that are changed or called very often. These functions, +`runhookdef(def, data)', `addhookdeffunc(def, func)', and +`deletehookdeffunc(def, func)' get a pointer to the `hookdef' +structure defining the hook instead of the name and otherwise behave +like their counterparts. + +Modules can also define function hooks. Other modules can then add +functions to these hooks to make the first module call these functions +instead of the default. + +Again, an array is used to define hooks: + + static struct hookdef foohooks[] = { + HOOKDEF("foo", foofunc, 0), + }; + +The first argument of the macro is the name of the hook. This name +is used whenever the hook is used. The second argument is the default +function for the hook or NULL if no default function exists. The +last argument is used to define flags for the hook. Currently only one +such flag is defined: `HOOKF_ALL'. If this flag is given and more than +one function was added to the hook, all functions will be called +(including the default function). Otherwise only the last function +added will be called. + +The functions that can be used as default functions or that can be +added to a hook have to be defined like: + + /**/ + static int + foofunc(Hookdef h, void *data) + { + ... + } + +The first argument is a pointer to the struct defining the hook. The +second argument is an arbitrary pointer that is given to the function +used to invoke hooks (see below). + +The functions to register and de-register hooks look like those for +the other things that can be defined by modules: + + /**/ + int + boot_foo(Module m) + { + int ret; + + ret = addhookdefs(m->nam, foohooks, sizeof(foohooks)/sizeof(*foohooks)) + ... + } + ... + /**/ + int + cleanup_foo(Module m) + { + deletehookdefs(m->nam, foohooks, sizeof(foohooks)/sizeof(*foohooks)); + ... + } + +Modules that define hooks can invoke the function(s) registered for +them by calling the function `runhook(name, data)'. The first argument +is the name of the hook and the second one is the pointer given to the +hook functions as their second argument. Hooks that have the `HOOKF_ALL' +flag call all function defined for them until one returns non-zero. +The return value of `runhook()' is the return value of the last hook +function called or zero if none was called. + +To add a function to a hook, the function `addhookfunc(name, func)' is +called with the name of the hook and a hook function as arguments. +Deleting them is done by calling `deletehookfunc(name, func)' with the +same arguments as for the corresponding call to `addhookfunc()'. + +Alternative forms of the last three function are provided for hooks +that are changed or called very often. These functions, +`runhookdef(def, data)', `addhookdeffunc(def, func)', and +`deletehookdeffunc(def, func)' get a pointer to the `hookdef' +structure defining the hook instead of the name and otherwise behave +like their counterparts. + +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 +------------- + +* Edit only the .yo files. All other formats (man pages, TeXinfo, HTML, + etc.) are automatically generated from the yodl source. + +* Always use the correct markup. em() is used for emphasis, and bf() + for citations. tt() marks text that is literal input to or output + from the shell. var() marks metasyntactic variables. + +* In addition to appropriate markup, always use quotes (`') where + appropriate. Specifically, use quotes to mark text that is not a part + of the actual text of the documentation (i.e., that it is being quoted). + In principle, all combinations of quotes and markup are possible, + because the purposes of the two devices are completely orthogonal. + For example, + + Type `tt(xyzzy)' to let zsh know you have played tt(advent). + Saying `plugh' aloud doesn't have much effect, however. + + In this case, "zsh" is normal text (a name), "advent" is a command name + ocurring in the main text, "plugh" is a normal word that is being quoted + (it's the user that says `plugh', not the documentation), and "xyzzy" + is some text to be typed literally that is being quoted. + +* For multiple-line pieces of text that should not be filled, there are + various models. + - If the text is pure example, i.e. with no metasyntactic variables etc., + it should be included within `example(...)'. The text will be + indented, will not be filled and will be put into a fixed width font. + - If the text includes mixed fonts, it should be included within + `indent(...)'. The text is now filled unless `nofill(...)' is also + used, and explicit font-changing commands are required inside. + - If the text appears inside some other format, such as for example the + `item()' list structure, then the instruction `nofill(...)', which + simply turns off filling should be used; as with `indent(...)', + explicit font changing commands are required. This can be used + without `indent()' when no identation is required, e.g. if the + accumulated indentation would otherwise be too long. + All the above should appear on their own, separated by newlines from the + surrounding text. No extra newlines after the opening or before the + closing parenthesis are required. |