Zsh Mailing List Archive
Messages sorted by: Reverse Date, Date, Thread, Author

PATCH: unloading modules



Hello

Below is a patch that tries to get the module-unloading stuff right.

Module unloading for modules that have wrapper functions that are
currently on the call stack is delayed until all wrappers have
finished. For modules that define wrappers the initialization and
finalization convention is slightly changed if the module has some
(module-)global state that is used by the wrapper function(s).
If a module defines the function `setup_<modulename>' this function is 
called directly after loading the module (and only then). The function 
`finish_<modulename>' is called directly before unloading a
module. Here I mean the *real* loading and unloading, not just a call
to zmodload.
The boot- and cleanup-functions are still called whenever the user
calls zmodload (-u).
With this setup and finish should be used to handle the global state
(allocating, freeing, whatever) and boot and cleanup should be used to 
register and de-register builtins, condition codes, and wrappers.

The patch also includes:
 - three helper functions in cond.c that will be good to have in
   functions implementing conditions; I planned to send them as a
   separate text, but this collided with
 - quite a bit of text about modules in Util/zsh-development-guide
   (the main implementors of the module stuff may want to have a look
   at this)
 - some hunks for moving some of the `#ifdef DYNAMIC's around (trying
   it with a statically linked zsh gave some warnings about stuff in
   module.pro without following definition but this was to be
   expected)

Finally I added some code for the setup- and finish-thing inside the
`#ifdef AIXDYNAMIC' but I'm far from certain that this is correct (is
the calling convention for boot et al different under AIX??). I don't
have a AIX box available so I need your help here.

Bye
 Sven

diff -c os/exec.c Src/exec.c
*** os/exec.c	Tue Dec 15 13:13:49 1998
--- Src/exec.c	Tue Dec 15 14:10:41 1998
***************
*** 2771,2789 ****
      char *ou;
  
      while (wrap) {
! 	wrap->module->flags |= MOD_WRAPPER;
! 	wrap->count++;
  	cont = wrap->handler(list, wrap->next, name);
! 	wrap->count--;
! 	if (!wrap->count) {
! 	    wrap->module->flags &= ~MOD_WRAPPER;
  #ifdef DYNAMIC
! 	    if (wrap->module->flags & MOD_UNLOAD) {
! 		wrap->module->flags &= ~MOD_UNLOAD;
! 		unload_module(wrap->module, NULL);
! 	    }
  #endif
- 	}
  	if (!cont)
  	    return;
  	wrap = wrap->next;
--- 2771,2784 ----
      char *ou;
  
      while (wrap) {
! 	wrap->module->wrapper++;
  	cont = wrap->handler(list, wrap->next, name);
! 	wrap->module->wrapper--;
  #ifdef DYNAMIC
! 	if (!wrap->module->wrapper &&
! 	    (wrap->module->flags & MOD_UNLOAD))
! 	    unload_module(wrap->module, NULL);
  #endif
  	if (!cont)
  	    return;
  	wrap = wrap->next;
*** os/module.c	Tue Dec 15 13:13:51 1998
--- Src/module.c	Tue Dec 15 16:41:11 1998
***************
*** 114,120 ****
      w->next = NULL;
      w->flags |= WRAPF_ADDED;
      w->module = m;
-     w->count = 0;
  
      return 0;
  }
--- 114,119 ----
***************
*** 265,279 ****
--- 264,286 ----
  #endif
  
  #ifdef DLSYM_NEEDS_UNDERSCORE
+ # define STR_SETUP     "_setup_"
+ # define STR_SETUP_S   "_setup_%s"
  # define STR_BOOT      "_boot_"
  # define STR_BOOT_S    "_boot_%s"
  # define STR_CLEANUP   "_cleanup_"
  # define STR_CLEANUP_S "_cleanup_%s"
+ # define STR_FINISH    "_finish_"
+ # define STR_FINISH_S  "_finish_%s"
  #else /* !DLSYM_NEEDS_UNDERSCORE */
+ # define STR_SETUP     "setup_"
+ # define STR_SETUP_S   "setup_%s"
  # define STR_BOOT      "boot_"
  # define STR_BOOT_S    "boot_%s"
  # define STR_CLEANUP   "cleanup_"
  # define STR_CLEANUP_S "cleanup_%s"
+ # define STR_FINISH    "finish_"
+ # define STR_FINISH_S  "finish_%s"
  #endif /* !DLSYM_NEEDS_UNDERSCORE */
  
  #endif /* !AIXDYNAMIC */
***************
*** 356,361 ****
--- 363,375 ----
  #ifdef AIXDYNAMIC
  
  /**/
+ static void
+ setup_module(Module m)
+ {
+     return ((int (*)_((int,Module))) m->handle)(2, m);
+ }
+ 
+ /**/
  static int
  init_module(Module m)
  {
***************
*** 369,379 ****
      return ((int (*)_((int,Module))) m->handle)(0, m);
  }
  
  #else
  
! /**/
! static int
! init_module(Module m)
  {
      char *s, *t;
  #ifndef DYNAMIC_NAME_CLASH_OK
--- 383,399 ----
      return ((int (*)_((int,Module))) m->handle)(0, m);
  }
  
+ /**/
+ static void
+ finish_module(Module m)
+ {
+     return ((int (*)_((int,Module))) m->handle)(3, m);
+ }
+ 
  #else
  
! static Module_func
! module_func(Module m, char *name, char *name_s)
  {
      char *s, *t;
  #ifndef DYNAMIC_NAME_CLASH_OK
***************
*** 389,401 ****
      if ((t = strrchr(s, '.')))
  	*t = '\0';
  #ifdef DYNAMIC_NAME_CLASH_OK
!     fn = (Module_func) dlsym(m->handle, STR_BOOT);
  #else /* !DYNAMIC_NAME_CLASH_OK */
      if (strlen(s) + 6 > PATH_MAX)
! 	return 1;
!     sprintf(buf, STR_BOOT_S, s);
      fn = (Module_func) dlsym(m->handle, buf);
  #endif /* !DYNAMIC_NAME_CLASH_OK */
      if(fn)
  	return fn(m);
      zwarnnam(m->nam, "no boot function", NULL, 0);
--- 409,440 ----
      if ((t = strrchr(s, '.')))
  	*t = '\0';
  #ifdef DYNAMIC_NAME_CLASH_OK
!     fn = (Module_func) dlsym(m->handle, name);
  #else /* !DYNAMIC_NAME_CLASH_OK */
      if (strlen(s) + 6 > PATH_MAX)
! 	return NULL;
!     sprintf(buf, name_s, s);
      fn = (Module_func) dlsym(m->handle, buf);
  #endif /* !DYNAMIC_NAME_CLASH_OK */
+     return fn;
+ }
+ 
+ /**/
+ static void
+ setup_module(Module m)
+ {
+     Module_func fn = module_func(m, STR_SETUP, STR_SETUP_S);
+ 
+     if (fn)
+ 	fn(m);
+ }
+ 
+ /**/
+ static int
+ init_module(Module m)
+ {
+     Module_func fn = module_func(m, STR_BOOT, STR_BOOT_S);
+ 
      if(fn)
  	return fn(m);
      zwarnnam(m->nam, "no boot function", NULL, 0);
***************
*** 406,438 ****
  static int
  cleanup_module(Module m)
  {
!     char *s, *t;
! #ifndef DYNAMIC_NAME_CLASH_OK
!     char buf[PATH_MAX + 1];
! #endif
!     Module_func fn;
  
-     s = strrchr(m->nam, '/');
-     if (s)
- 	s = dupstring(++s);
-     else
- 	s = m->nam;
-     if ((t = strrchr(s, '.')))
- 	*t = '\0';
- #ifdef DYNAMIC_NAME_CLASH_OK
-     fn = (Module_func) dlsym(m->handle, STR_CLEANUP);
- #else /* !DYNAMIC_NAME_CLASH_OK */
-     if (strlen(s) + 9 > PATH_MAX)
- 	return 1;
-     sprintf(buf, STR_CLEANUP_S, s);
-     fn = (Module_func) dlsym(m->handle, buf);
- #endif /* !DYNAMIC_NAME_CLASH_OK */
      if(fn)
  	return fn(m);
      zwarnnam(m->nam, "no cleanup function", NULL, 0);
      return 1;
  }
  
  #endif /* !AIXDYNAMIC */
  
  /**/
--- 445,470 ----
  static int
  cleanup_module(Module m)
  {
!     Module_func fn = module_func(m, STR_CLEANUP, STR_CLEANUP_S);
  
      if(fn)
  	return fn(m);
      zwarnnam(m->nam, "no cleanup function", NULL, 0);
      return 1;
  }
  
+ /**/
+ static void
+ finish_module(Module m)
+ {
+     Module_func fn = module_func(m, STR_FINISH, STR_FINISH_S);
+ 
+     if (fn)
+ 	fn(m);
+ 
+     dlclose(m->handle);
+ }
+ 
  #endif /* !AIXDYNAMIC */
  
  /**/
***************
*** 449,456 ****
  	m = zcalloc(sizeof(*m));
  	m->nam = ztrdup(name);
  	m->handle = handle;
  	if (init_module(m)) {
! 	    dlclose(handle);
  	    zsfree(m->nam);
  	    zfree(m, sizeof(*m));
  	    return NULL;
--- 481,489 ----
  	m = zcalloc(sizeof(*m));
  	m->nam = ztrdup(name);
  	m->handle = handle;
+ 	setup_module(m);
  	if (init_module(m)) {
! 	    finish_module(m);
  	    zsfree(m->nam);
  	    zfree(m, sizeof(*m));
  	    return NULL;
***************
*** 459,467 ****
  	    addlinknode(modules, m);
  	} LASTALLOC;
  	return m;
!     }
      m = (Module) getdata(node);
!     if (m->handle)
  	return m;
      if (m->flags & MOD_BUSY) {
  	zerr("circular dependencies for module %s", name, 0);
--- 492,504 ----
  	    addlinknode(modules, m);
  	} LASTALLOC;
  	return m;
!     } 
      m = (Module) getdata(node);
!     if (m->flags & MOD_UNLOAD) {
! 	if (init_module(m))
! 	    return NULL;
! 	m->flags &= ~MOD_UNLOAD;
!     } else if (m->handle)
  	return m;
      if (m->flags & MOD_BUSY) {
  	zerr("circular dependencies for module %s", name, 0);
***************
*** 756,766 ****
  int
  unload_module(Module m, LinkNode node)
  {
!     if (m->handle && cleanup_module(m))
  	return 1;
      else {
  	if (m->handle)
! 	    dlclose(m->handle);
  	m->handle = NULL;
  	if(!m->deps) {
  	    if (!node) {
--- 793,808 ----
  int
  unload_module(Module m, LinkNode node)
  {
!     if (m->handle && !(m->flags & MOD_UNLOAD) && cleanup_module(m))
  	return 1;
      else {
+ 	if (m->wrapper) {
+ 	    m->flags |= MOD_UNLOAD;
+ 	    return 0;
+ 	}
+ 	m->flags &= ~MOD_UNLOAD;
  	if (m->handle)
! 	    finish_module(m);
  	m->handle = NULL;
  	if(!m->deps) {
  	    if (!node) {
***************
*** 794,800 ****
  
  		for (mn = firstnode(modules); mn; incnode(mn)) {
  		    m = (Module) getdata(mn);
! 		    if (m->deps && m->handle)
  			for (dn = firstnode(m->deps); dn; incnode(dn))
  			    if (!strcmp((char *) getdata(dn), *args)) {
  				zwarnnam(nam, "module %s is in use by another module and cannot be unloaded", *args, 0);
--- 836,842 ----
  
  		for (mn = firstnode(modules); mn; incnode(mn)) {
  		    m = (Module) getdata(mn);
! 		    if (!(m->flags & MOD_UNLOAD) && m->deps && m->handle)
  			for (dn = firstnode(m->deps); dn; incnode(dn))
  			    if (!strcmp((char *) getdata(dn), *args)) {
  				zwarnnam(nam, "module %s is in use by another module and cannot be unloaded", *args, 0);
***************
*** 803,814 ****
  			    }
  		}
  		m = (Module) getdata(node);
! 		if (!(m->flags & MOD_WRAPPER)) {
! 		    if (unload_module(m, node))
! 			ret = 1;
! 		}
! 		else
! 		    m->flags |= MOD_UNLOAD;
  	    } else if (!ops['i']) {
  		zwarnnam(nam, "no such module %s", *args, 0);
  		ret = 1;
--- 845,852 ----
  			    }
  		}
  		m = (Module) getdata(node);
! 		if (unload_module(m, node))
! 		    ret = 1;
  	    } else if (!ops['i']) {
  		zwarnnam(nam, "no such module %s", *args, 0);
  		ret = 1;
***************
*** 820,826 ****
  	/* list modules */
  	for (node = firstnode(modules); node; incnode(node)) {
  	    m = (Module) getdata(node);
! 	    if (m->handle) {
  		if(ops['L']) {
  		    printf("zmodload ");
  		    if(m->nam[0] == '-')
--- 858,864 ----
  	/* list modules */
  	for (node = firstnode(modules); node; incnode(node)) {
  	    m = (Module) getdata(node);
! 	    if (m->handle && !(m->flags & MOD_UNLOAD)) {
  		if(ops['L']) {
  		    printf("zmodload ");
  		    if(m->nam[0] == '-')
***************
*** 835,842 ****
      } else {
  	/* load modules */
  	for (; *args; args++) {
  	    node = find_module(*args);
! 	    if (node && ((Module) getdata(node))->handle) {
  		if (!ops['i']) {
  		    zwarnnam(nam, "module %s already loaded.", *args, 0);
  		    ret = 1;
--- 873,883 ----
      } else {
  	/* load modules */
  	for (; *args; args++) {
+ 	    Module m;
+ 
  	    node = find_module(*args);
! 	    if (node && (m = ((Module) getdata(node)))->handle &&
! 		!(m->flags & MOD_UNLOAD)) {
  		if (!ops['i']) {
  		    zwarnnam(nam, "module %s already loaded.", *args, 0);
  		    ret = 1;
***************
*** 894,901 ****
      return p;
  }
  
- #ifdef DYNAMIC
- 
  /* This adds the given condition definition. The return value is zero on *
   * success and 1 on failure. If there is a matching definition for an    *
   * autoloaded condition, it is removed. */
--- 935,940 ----
***************
*** 909,918 ****
      if (p) {
  	if (!p->module || (p->flags & CONDF_ADDED))
  	    return 1;
! 
  	/* There is an autoload definition. */
  
  	deleteconddef(p);
      }
      c->next = condtab;
      condtab = c;
--- 948,958 ----
      if (p) {
  	if (!p->module || (p->flags & CONDF_ADDED))
  	    return 1;
! #ifdef DYNAMIC
  	/* There is an autoload definition. */
  
  	deleteconddef(p);
+ #endif
      }
      c->next = condtab;
      condtab = c;
***************
*** 942,947 ****
--- 982,989 ----
      return hadf ? hads : 1;
  }
  
+ #ifdef DYNAMIC
+ 
  /* This adds a definition for autoloading a module for a condition. */
  
  /**/
***************
*** 1014,1017 ****
      return hadf ? hads : 1;
  }
  
! #endif /* DYNAMIC */
--- 1056,1059 ----
      return hadf ? hads : 1;
  }
  
! #endif
diff -c os/zsh.h Src/zsh.h
*** os/zsh.h	Tue Dec 15 13:13:52 1998
--- Src/zsh.h	Tue Dec 15 14:19:40 1998
***************
*** 781,793 ****
      int flags;
      WrapFunc handler;
      Module module;
-     int count;
  };
  
  #define WRAPF_ADDED 1
  
  #define WRAPDEF(func) \
!     { NULL, 0, func, NULL, 0 }
  
  /* node in builtin command hash table (builtintab) */
  
--- 781,792 ----
      int flags;
      WrapFunc handler;
      Module module;
  };
  
  #define WRAPF_ADDED 1
  
  #define WRAPDEF(func) \
!     { NULL, 0, func, NULL }
  
  /* node in builtin command hash table (builtintab) */
  
***************
*** 836,846 ****
      int flags;
      void *handle;
      LinkList deps;
  };
  
  #define MOD_BUSY    (1<<0)
! #define MOD_WRAPPER (1<<1)
! #define MOD_UNLOAD  (1<<2)
  
  /* node used in parameter hash table (paramtab) */
  
--- 835,845 ----
      int flags;
      void *handle;
      LinkList deps;
+     int wrapper;
  };
  
  #define MOD_BUSY    (1<<0)
! #define MOD_UNLOAD  (1<<1)
  
  /* node used in parameter hash table (paramtab) */
  
*** os/cond.c	Tue Dec 15 13:13:49 1998
--- Src/cond.c	Tue Dec 15 15:45:25 1998
***************
*** 244,246 ****
--- 244,281 ----
      else
  	return isset(i);
  }
+ 
+ /**/
+ char *
+ cond_str(char **args, int num)
+ {
+     char *s = args[num];
+ 
+     singsub(&s);
+     untokenize(s);
+ 
+     return s;
+ }
+ 
+ /**/
+ long
+ cond_val(char **args, int num)
+ {
+     char *s = args[num];
+ 
+     singsub(&s);
+     untokenize(s);
+ 
+     return matheval(s);
+ }
+ 
+ /**/
+ int
+ cond_match(char **args, int num, char *str)
+ {
+     char *s = args[num];
+ 
+     singsub(&s);
+ 
+     return matchpat(str, s);
+ }
*** os/Modules/example.c	Tue Dec 15 14:35:54 1998
--- Src/Modules/example.c	Tue Dec 15 15:48:43 1998
***************
*** 53,66 ****
  static int
  cond_p_len(Conddef c, char **a)
  {
!     char *s1 = a[0], *s2 = a[1];
  
!     singsub(&s1);
!     untokenize(s1);
!     if (s2) {
! 	singsub(&s2);
! 	untokenize(s2);
! 	return strlen(s1) == matheval(s2);
      } else {
  	return !s1[0];
      }
--- 53,64 ----
  static int
  cond_p_len(Conddef c, char **a)
  {
!     char *s1 = cond_str(a, 0);
  
!     if (a[1]) {
! 	long v = cond_val(a, 1);
! 
! 	return strlen(s1) == v;
      } else {
  	return !s1[0];
      }
***************
*** 70,81 ****
  static int
  cond_i_ex(Conddef c, char **a)
  {
!     char *s1 = a[0], *s2 = a[1];
  
-     singsub(&s1);
-     untokenize(s1);
-     singsub(&s2);
-     untokenize(s2);
      return !strcmp("example", dyncat(s1, s2));
  }
  
--- 68,75 ----
  static int
  cond_i_ex(Conddef c, char **a)
  {
!     char *s1 = cond_str(a, 0), *s2 = cond_str(a, 1);
  
      return !strcmp("example", dyncat(s1, s2));
  }
  
*** Util/zsh-development-guide.old	Tue Dec 15 14:49:45 1998
--- Util/zsh-development-guide	Tue Dec 15 16:23:11 1998
***************
*** 112,117 ****
--- 112,368 ----
    groups of statements in the interests of clarity.  There should never
    be two consecutive blank lines.
  
+ Modules
+ -------
+ 
+ Modules should define should define two functions which will
+ automatically called by the zsh core. The first one, named `boot_foo'
+ for a module name `foo', should register all builtins, conditional
+ codes, and function wrappers. The second one, named `cleanup_foo' for
+ module `foo' is called when the module is unloaded and should
+ de-register the builtins etc. Since this second function is only
+ executed when the module is used as an dynamically loaded module you
+ should surround it bei `#ifdef MODULE' and `#endif'.
+ Both of these functions should return zero if they succeded 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, 1, 2, cond_p_len),
+     CONDDEF("ex", CONDF_INFIX, 0, 0, cond_i_ex),
+   };
+ 
+ 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)
+   - 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 the C-function implementing the
+     conditional is given
+ 
+ The definition for the function looks like:
+ 
+   /**/
+   static int
+   cond_p_len(Conddef c, char **a)
+   {
+     ...
+   }
+ 
+ The first argument is the table entry defining the condition code and
+ the second argument is an array containing the strings
+ (NULL-terminated like the array of arguments for builtins).
+ 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;
+   }
+ 
+ There is a problem when the user tries to unload a module that has
+ defined wrapper 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 
+ finalised (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.
+ Therefore two more module-functions may be called from zsh. When a
+ module is really loaded zsh first tries to run the function
+ `setup_foo' (for module `foo'). After that the function `boot_foo' is
+ run and this function is also run if a module is still in memory but
+ the user issued a `zmodload -u' call for it (i.e. zsh detected that it 
+ can't immediately unload the module). Correspondingly, the cleanup
+ function is run whenever the user calls `zmodload -u' for a module and 
+ the function `finish_foo' (for module `foo') only if the module is
+ actually unloaded. So, if you have some state accessed by a wrapper
+ function put the initialization for it in the setup-function and the
+ finalization for it in the finish-function. The boot- and
+ cleanup-function are then only used to register and de-register
+ builtins, condition codes, and wrapper functions. This way it is
+ guaranteed that the data needed by the wrapper function is still valid 
+ when wrappers are active and that the user-visible interface defined
+ by the module disappears as soon as the user tries to unload a module.
+ 
  Documentation
  -------------
  

--
Sven Wischnowsky                         wischnow@xxxxxxxxxxxxxxxxxxxxxxx



Messages sorted by: Reverse Date, Date, Thread, Author