summaryrefslogtreecommitdiff
path: root/Etc/zsh-development-guide
diff options
context:
space:
mode:
Diffstat (limited to 'Etc/zsh-development-guide')
-rw-r--r--Etc/zsh-development-guide673
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.