Mirror of the gdb-patches mailing list
 help / color / mirror / Atom feed
* RE: [PATCH] plugin patch
@ 2002-11-06  7:47 Howell, David P
  2002-11-06  7:55 ` Daniel Jacobowitz
  2002-11-06 11:12 ` Andrew Cagney
  0 siblings, 2 replies; 19+ messages in thread
From: Howell, David P @ 2002-11-06  7:47 UTC (permalink / raw)
  To: Scott Moser; +Cc: gdb-patches


On Mon, 28 Oct 2002, Scott Moser wrote:

> Below is a patch to add plugin support to GDB.  It exports a fairly
> simple programmable interface for people to extend the functionality
of
> GDB via runtime loaded shared libraries in ways that may not fit with
> the direction of the main GDB tree (not cross-platform, not stable,
> niche audience...).
For folks like myself that are working on an alternate architecture or 
runtime support components (in this case NGPT threads) this would be 
very useful, as I can see several info commands that I would like to 
add for M:N user mode scheduling state and LWP state display that would 
be unique to NGPT and it's implementation. 

Instead of having to add this to the standard gdb as a one-off for NGPT,
I can use the standard gdb and design them as plug-ins to be loaded only

when debugging NGPT applications. This feels a lot cleaner and could be 
applied for other gdb features/architectures to keep the core as small 
and efficient as possible, loading additional support/features on demand
only when needed.

Thanks,
Dave Howell


-----Original Message-----
From: Scott Moser [mailto:ssmoser@us.ibm.com] 
Sent: Wednesday, November 06, 2002 9:45 AM
To: Eli Zaretskii
Cc: gdb-patches@sources.redhat.com
Subject: Re: [PATCH] plugin patch

All,
   I sent a patch to add plugin support to GDB under this subject last
week, and hadn't received a response other than the one below.
   I'll gladly document the plugin in gdbint.texinfo if needed.  IBM
made a copyright assignment agreement previously and I have the required
papers to assign this patch also.

   I don't think that beneficial functionality should be kept from gdb
simply because people might use it in unfriendly ways.  Anyone
interested in taking gdb functionality could still get the GDB source
and highjack that (which would even be less obvious to detect than
shipping a file and telling the user to "plugin load" it within GDB).

   If my patch was submitted incorrectly, there was something else
that I was missing, or anything else I'd need to change/update/add,
please let me know.

   Thanks,
   Scott


On Tue, 29 Oct 2002, Eli Zaretskii wrote:

>
> On Mon, 28 Oct 2002, Scott Moser wrote:
>
> > Below is a patch to add plugin support to GDB.  It exports a fairly
> > simple programmable interface for people to extend the functionality
of
> > GDB via runtime loaded shared libraries in ways that may not fit
with
> > the direction of the main GDB tree (not cross-platform, not stable,
> > niche audience...).
>
> IIRC, the FSF doesn't like to add to GNU software support for
dynamically
> loading arbitrary modules (for fear of non-free libraries being used
thru
> this).
>
> In any case, if this is approved, please consider documenting it in
> gdbint.texinfo.  TIA
>
>

Scott Moser
Software Engineer; Linux Technology Center
IBM Corp., Austin, Tx
(512) 838-1533   T/L: 678-1533
ssmoser@us.ibm.com , internal zip: 9812


^ permalink raw reply	[flat|nested] 19+ messages in thread
* [PATCH] plugin patch
@ 2002-10-28 14:01 Scott Moser
  2002-10-28 22:28 ` Eli Zaretskii
  2002-11-06 15:56 ` Andrew Cagney
  0 siblings, 2 replies; 19+ messages in thread
From: Scott Moser @ 2002-10-28 14:01 UTC (permalink / raw)
  To: gdb-patches

[-- Attachment #1: Type: TEXT/PLAIN, Size: 19691 bytes --]

Below is a patch to add plugin support to GDB.  It exports a fairly
simple programmable interface for people to extend the functionality of
GDB via runtime loaded shared libraries in ways that may not fit with
the direction of the main GDB tree (not cross-platform, not stable,
niche audience...).

Also attached are two example plugins.  They've been tested on linux and
AIX, and some time ago on freebsd.

Changelog entry:

2002-10-28  Scott Moser <ssmoser@us.ibm.com>

	* Makefile.in (plugin.o): Add build line for plugin.c
	* configure.in: Add --enable-plugin option
	* config/powerpc/aix.mh: add -Wl,-bexpall on aix build
	* plugin.c: new file, plugin implementation
	* plugin.h: new file, header file for plugin.c
	* gdbplugin.h: new file for plugin use

gdb plugin architecture overview
- gdb user commands
   - plugin load
      takes a filename and loads it as a gdb plugin. searches the
      plugin-path if needed
   - plugin description
      takes a filename or number (of a loaded plugin) and calls its
      plugin_description function. if not implemented returns "description N/A"
   - plugin name
      takes a filename or number (of a loaded plugin) and calls its
      plugin_name function. if not implemented returns "name N/A"
   - plugin commands
      takes a filename or number (of a loaded plugin) and calls its
      plugin_commands function.  if not implmemented returns "commands N/A"
   - plugin list
      takes no arguments.  lists the currently loaded plugins.
   - plugin search [ verbose ]
      trys to open each file in the current plugin-path as a gdb plugin.
      lists filename and plugin-name for each plugin found.
      if 'verbose' is passed in, also call's plugins' 'description'
   - set plugin-path
      takes a string and sets it to the ":" delimited search path

- functions implemented by gdb plugins
   - plugin_init (required)
      prototype: int plugin_init(const char * version, char * args, int tty)
         - version is character string describing GDB's version
         - args are user specified arguments after the filename in 'plugin load'
         - tty is gdb's tty
         - returns true or false on success/failure of load
         - It is expected that in this function, the plugin will perform
           actions like:
            - on systems where '-rdynamic'-type symbol resolution isn't
              available, use dlsym() to access the functions it requires within
              gdb (or, potentially, other plugins).  For example, the functions
              "add_cmd", "add_set_cmd", etc.
            - call "add_cmd" to add its plugin-specific commands to the gdb
              command set
            - call "add_set_cmd" to enable plugin-specific variables
      called at load of a plugin

   - plugin_name
      prototype: const char * plugin_name(void)
         - takes no arguments
         - returns a character string (short) "name" for the plugin.
      called by 'plugin "name|list|search"'

   - plugin_commands
      prototype: const char * plugin_commands(void)
         - takes no arguments
         - returns a string describing the list of commands registered (or that
           will be registered) by the plugin.
      called by 'plugin commands'

   - plugin_description
      prototype: const char * plugin_description(void)
         - takes no arguments
         - returns a character string "description" of the command.
      called by 'plugin "name|list|search verbose"'


- implementation notes
   - expect users to call 'plugin_commands', 'plugin_description', and
     'plugin_name' before plugin_init is called.
     For example, by 'plugin search'
   - plugin_init will only be called on load.
   - if you check the version passed in to decide if you'll run or not,
     consider offering a 'force' option to load even if you don't think you
     should

- issues:
   - colon ":" is the search path separator.  this may cause problems on
     windows with its C:\ like filenames.  At this point, the plugin design
     will not work on windows anyway, as windows requires explicitly exporting
     functions for them to be accessible outside of main (no -rdynamic)
   - uses printf_filtered
   - the version string format changes between releases and cvs

Scott Moser
Software Engineer; Linux Technology Center
IBM Corp., Austin, Tx
(512) 838-1533   T/L: 678-1533
ssmoser@us.ibm.com , internal zip: 9812


diff -uprN ../src-20021028.sparce/gdb/Makefile.in gdb/Makefile.in
--- ../src-20021028.sparce/gdb/Makefile.in	2002-10-28 13:53:34.000000000 -0600
+++ gdb/Makefile.in	2002-10-28 14:46:43.000000000 -0600
@@ -1968,6 +1968,9 @@ parse.o: parse.c $(defs_h) $(gdb_string_
 	$(frame_h) $(expression_h) $(value_h) $(command_h) $(language_h) \
 	$(parser_defs_h) $(gdbcmd_h) $(symfile_h) $(inferior_h) \
 	$(doublest_h) $(builtin_regs_h) $(gdb_assert_h)
+plugin.o: plugin.c $(defs_h) $(frame_h) $(inferior_h) $(target_h) $(gdbcmd_h) \
+	$(language_h) $(symfile_h) $(objfiles_h) $(completer_h) $(value_h) \
+	$(gdb_string_h) $(gdbcore_h) $(gdb_stat_h) $(xcoffsolib_h)
 ppc-bdm.o: ppc-bdm.c $(defs_h) $(gdbcore_h) $(gdb_string_h) $(frame_h) \
 	$(inferior_h) $(bfd_h) $(symfile_h) $(target_h) $(gdbcmd_h) \
 	$(objfiles_h) $(gdb_stabs_h) $(serial_h) $(ocd_h) $(ppc_tdep_h) \
diff -uprN ../src-20021028.sparce/gdb/config/powerpc/aix.mh gdb/config/powerpc/aix.mh
--- ../src-20021028.sparce/gdb/config/powerpc/aix.mh	2002-10-28 13:53:34.000000000 -0600
+++ gdb/config/powerpc/aix.mh	2002-10-28 13:26:31.000000000 -0600
@@ -6,6 +6,7 @@ NAT_FILE= nm-aix.h
 NATDEPFILES= fork-child.o infptrace.o inftarg.o corelow.o rs6000-nat.o \
 	     xcoffread.o xcoffsolib.o

+LOADLIBES= -Wl,-bexpall
 # When compiled with cc, for debugging, this argument should be passed.
 # We have no idea who our current compiler is though, so we skip it.
 # MH_CFLAGS = -bnodelcsect
diff -uprN ../src-20021028.sparce/gdb/configure.in gdb/configure.in
--- ../src-20021028.sparce/gdb/configure.in	2002-10-28 13:53:34.000000000 -0600
+++ gdb/configure.in	2002-10-28 14:44:44.000000000 -0600
@@ -665,6 +665,30 @@ case ${enable_gdbmi} in
     ;;
 esac

+dnl Enable gdb plugin interface
+AC_ARG_ENABLE(plugin,
+[  --enable-plugin           Enable GDB plugin interface],
+[
+  case "${enable_plugin}" in
+    yes ) if test x$gdb_cv_os_cygwin = xyes; then
+            AC_MSG_ERROR(plugin will not work on win32; disabled)
+          fi ;;
+    no) ;;
+    "")  enable_plugin=yes ;;
+    *)
+      AC_MSG_ERROR(Bad value for --enable-plugin: ${enableval})
+    ;;
+  esac
+],
+[enable_plugin=yes])
+case ${enable_plugin} in
+  "yes" )
+      CONFIG_OBS="${CONFIG_OBS} plugin.o"
+      CONFIG_SRCS="${CONFIG_SRCS} plugin.c"
+      CONFIG_INITS="${CONFIG_INITS} plugin.c"
+    ;;
+esac
+
 # Configure UI_OUT by default (before 5.2 it can be disabled)
 # It must be configured if gdbmi is configured

diff -uprN ../src-20021028.sparce/gdb/gdbplugin.h gdb/gdbplugin.h
--- ../src-20021028.sparce/gdb/gdbplugin.h	1969-12-31 18:00:00.000000000 -0600
+++ gdb/gdbplugin.h	2002-10-28 13:26:31.000000000 -0600
@@ -0,0 +1,11 @@
+/* this file exists as a convienence for gdb plugin programming
+ * it will hopefully include all the gdb header files that a plugin
+ * developer would want to use
+ * */
+#include "defs.h"
+#include "gdbcmd.h"
+#include "gdbcore.h"
+#include "completer.h"
+#include "value.h"
+#include "valprint.h"
+#include "language.h"
diff -uprN ../src-20021028.sparce/gdb/plugin.c gdb/plugin.c
--- ../src-20021028.sparce/gdb/plugin.c	1969-12-31 18:00:00.000000000 -0600
+++ gdb/plugin.c	2002-10-28 13:26:31.000000000 -0600
@@ -0,0 +1,468 @@
+#include "plugin.h"
+#include "defs.h"
+#include "gdbcmd.h"
+#include "gdbcore.h"
+#include "completer.h"
+#include "dlfcn.h"
+#include "dirent.h"
+#include "version.h"
+#include <sys/stat.h>
+#include <string.h>
+
+static struct plugin* newPlugin(void *, const char *);
+static struct plugin* findPlugin(const char *);
+static struct plugin* findPluginByNum(int);
+static const char * searchPluginPath(const char * );
+static void * openPlugin(const char * ,int, char *, int );
+static void * getPluginHandle(const char *);
+static const char * callPluginInfoFunction(void *,char *,const char *fallback);
+
+static char *plugin_path;
+
+static struct plugin * pluginList;
+
+void
+plugin_list(int tty)
+{
+   /* list the plugins currently loaded */
+   const struct plugin * curp=pluginList;
+   if(curp==NULL)
+   {
+      printf_filtered("no plugins loaded\n");
+      return;
+   }
+   while(curp!=NULL)
+   {
+      printf_filtered("%i %s %s\n", curp->num, curp->filename, plugin_name(curp->handle));
+      curp=curp->next;
+   }
+   return;
+}
+
+const char *
+plugin_commands(void * handle)
+{
+   return callPluginInfoFunction(handle,"plugin_commands","commands N/A");
+}
+
+const char *
+plugin_name(void * handle)
+{
+   return callPluginInfoFunction(handle,"plugin_name","name N/A");
+}
+
+const char *
+plugin_description(void * handle)
+{
+   return callPluginInfoFunction(handle,"plugin_description","description N/A");
+}
+
+const char *
+callPluginInfoFunction(void * handle,char * funcname,const char *fallback)
+{
+   const char * (*func)(void);
+   const char * ret=NULL;
+   if(handle && funcname)
+   {
+      func=dlsym(handle,funcname);
+      if(func)
+      {
+         ret=func();
+      }
+   }
+   return (ret) ? ret : fallback;
+}
+
+void
+plugin_search(const char * str, int tty, int verbose)
+{
+   /* searches the current path for files with plugin_init() defined. */
+   if(plugin_path!=NULL)
+   {
+      const char *tok;
+      char *spath=(char*)malloc((strlen(plugin_path)+1)*sizeof(char));
+      if(spath==NULL)
+         return;
+      strcpy(spath,plugin_path);
+      tok=strtok(spath,":");
+      while(tok!=NULL)
+      {
+         struct dirent *direntry;
+         DIR * dir=opendir(tok);
+         if (dir!=NULL)
+         {
+            while((direntry=readdir(dir))!=NULL)
+            {
+#if (defined(DT_REG) && defined(DT_LNK) && defined(DT_UNKNOWN))
+               if (direntry->d_type==DT_REG || direntry->d_type==DT_LNK || direntry->d_type==DT_UNKNOWN)
+               {
+#endif
+                  void *handle=NULL;
+                  char *filename=(char*)malloc((strlen(tok)+strlen(direntry->d_name)+2)*sizeof(char));
+                  if(filename==NULL)
+                  {
+                     free(spath);
+                     return;
+                  }
+                  sprintf(filename,"%s/%s",tok,direntry->d_name);
+                  handle=openPlugin(filename,0,(char *)NULL, tty);
+                  if(handle!=NULL)
+                  {
+                     printf_filtered("%s : %s\n",filename,plugin_name(handle));
+                     if(verbose)
+                     {
+                        printf_filtered("\t%s\n",plugin_description(handle));
+                     }
+                     dlclose(handle);
+                  }
+                  free(filename);
+#if (defined(DT_REG) && defined(DT_LNK) && defined(DT_UNKNOWN))
+               }
+#endif
+            }
+         }
+         tok=strtok(NULL,":");
+      }
+   }
+   else
+   {
+      printf_filtered("plugin-path not set\n");
+   }
+   return;
+}
+
+void
+plugin_load(const char * filename, char * args, int tty)
+{
+   /* load a plugin named filename, (searching plugin-path if set )
+    * with args given
+    * */
+   void *handle=NULL;
+
+   handle=openPlugin(filename,1,args, tty);
+   if(handle==NULL)
+   {
+      const char *tempfile;
+      tempfile=searchPluginPath(filename);
+      if(tempfile==NULL)
+      {
+         printf_filtered("plugin load: couldn't find %s\n",filename);
+         return;
+      }
+      handle=openPlugin(tempfile,1,args,tty);
+      if(handle==NULL)
+      {
+         printf_filtered("plugin load: couldn't load %s\n",tempfile);
+         return;
+      }
+   }
+
+   newPlugin(handle,filename);
+   return;
+}
+
+void *
+getPluginHandle(const char * filename)
+{
+   struct plugin * cur;
+   void * handle;
+   cur=findPlugin(filename);
+   if(cur!=NULL)
+   {
+      handle=cur->handle;
+   }
+   else
+   {
+      const char * fullpath;
+      fullpath=searchPluginPath(filename);
+      handle=openPlugin(fullpath,0,(char *)NULL, 0);
+   }
+   return(handle);
+}
+
+struct plugin *
+newPlugin(void * handle, const char * filename)
+{
+   /* add this plugin at the beginning of the list */
+   struct plugin* newP;
+   newP=(struct plugin*)malloc(sizeof(struct plugin)+strlen(filename)*sizeof(char)+1);
+   if(newP==NULL)
+   {
+      printf_filtered("Failed to allocate space for plugin %s\n",filename);
+      return NULL;
+   }
+   newP->handle=handle;
+   newP->next=pluginList;
+   newP->filename=(char*)(newP+1);
+   strcpy(newP->filename,filename);
+   if(newP->next!=NULL)
+   {
+      newP->num=(int)((newP->next)->num)+1;
+   }
+   else
+   {
+      newP->num=0;
+   }
+   pluginList=newP;
+   return newP;
+}
+
+struct plugin *
+findPluginByNum(int num)
+{
+   struct plugin * cur;
+
+   for ( cur = pluginList; cur != NULL; cur = cur->next )
+   {
+      if (cur->num == num) break;
+   }
+   return cur;
+}
+
+struct plugin *
+findPlugin(const char * filename)
+{
+   /* find a plugin by filename from the plugin_list */
+   struct plugin * cur;
+
+   for ( cur = pluginList; cur != NULL; cur = cur->next )
+   {
+      if (strcmp(filename,cur->filename) == 0) break;
+   }
+   return cur;
+}
+
+void *
+openPlugin(const char * filename,int init, char * args, int tty)
+{
+   /* opens a named filename.  if init is true, will call its init function
+    * and pass in args and tty
+    * */
+   void * handle;
+   int (*initfunc)(const char *, char *, int);
+
+   if(filename==NULL)
+      return(NULL);
+
+   handle=dlopen(filename,RTLD_LAZY);
+   if(handle==NULL)
+      return(NULL);
+
+   if(!init)
+      return(handle);
+
+   initfunc=dlsym(handle,"plugin_init");
+
+   if(initfunc==NULL)
+   {
+      printf_filtered("couldn't find plugin_init in plugin %s\n",filename);
+      dlclose(handle);
+      return(NULL);
+   }
+
+   if(initfunc(version,args,tty))
+   {
+      printf_filtered("successfully loaded plugin %s\n",filename);
+      return(handle);
+   }
+   else
+   {
+      printf_filtered("plugin_init failed %s\n",filename);
+      /*
+       * don't do a dlclose here to avoid possible segfault if
+       * failed load didn't clean itself up correctly
+       */
+   }
+   return(NULL);
+}
+
+
+const char *
+searchPluginPath(const char * filename)
+{
+   /* returns a full path to a file if it exists in search path */
+   if(filename!=NULL)
+   {
+      int found;
+      struct stat buf;
+      char *tok;
+      char *spath;
+      if(!stat(filename,&buf))
+      {
+         return(filename);
+      }
+      if(plugin_path!=NULL)
+      {
+         spath=(char*)malloc((strlen(plugin_path)+1)*sizeof(char));
+         if(spath==NULL)
+         {
+            return(NULL);
+         }
+         strcpy(spath,plugin_path);
+         tok=strtok(spath,":");
+         while(tok!=NULL)
+         {
+            char *fp=(char*)malloc(((strlen(filename)+strlen(tok)+2))*sizeof(char));
+            if(fp==NULL)
+            {
+               free(spath);
+               return(NULL);
+            }
+            sprintf(fp,"%s/%s",tok,filename);
+            if(!stat(fp,&buf))
+            {
+               free(spath);
+               return(fp);
+            }
+            free(fp);
+            tok=strtok(NULL, ":");
+         }
+         free(spath);
+      }
+   }
+   return(NULL);
+}
+
+void
+plugin_command (char *arg, int from_tty)
+{
+   /* top level 'plugin' command handler */
+   char * cmdtype;
+   char * filename;
+   char * cmdargs = NULL;
+   int start;
+   int arglen;
+   int plugNum;
+   char *endptr;
+   struct plugin *p;
+   void * dlhandle;
+   const char * retstr;
+
+   if(arg==NULL)
+   {
+      printf_filtered("plugin: need second level command (see 'help plugin')\n");
+      return;
+   }
+
+   if (!strcmp(arg,"search verbose"))
+   {
+      plugin_search("",from_tty,1);
+      return;
+   }
+   else if (!strcmp(arg,"search"))
+   {
+      plugin_search("",from_tty,0);
+      return;
+   }
+   else if (!strcmp(arg,"list"))
+   {
+      plugin_list(from_tty);
+      return;
+   }
+
+   // all remaining plugin commands require args
+   cmdtype=arg;
+   if ((endptr = strchr(arg,' ')) == NULL)
+   {
+         printf_filtered("plugin: Error no argument to plugin command \"%s\"\n",cmdtype);
+         return ;
+   }
+   *endptr = '\0';
+   filename = endptr + 1;
+   while(filename[0]!='\0' && filename[0]==' ') filename++;
+
+   if(filename[0]=='\0')
+      return ;
+
+   if(filename[0]=='"' && ((endptr=strchr(filename+1,'"'))!=NULL))
+   {
+         filename++;
+         *endptr='\0';
+         cmdargs=endptr+1;
+         if(endptr[1]=='\0') cmdargs=NULL;
+   }
+   else
+   {
+      if((endptr=strchr(filename,' '))!=NULL)
+      {
+         *endptr = '\0';
+         cmdargs=endptr+1;
+      }
+   }
+
+   if(!strcmp(cmdtype,"load"))
+   {
+      plugin_load(filename,cmdargs,from_tty);
+      return;
+   }
+
+   plugNum=(int)strtol(filename,&endptr,10);
+
+   if(endptr!=NULL && (*endptr)=='\0')
+   {
+      p=findPluginByNum(plugNum);
+      if(p==NULL)
+      {
+         printf_filtered("no plugin number %i loaded (\"plugin list\" for list)\n",plugNum);
+         return;
+      }
+      dlhandle=p->handle;
+   }
+   else
+   {
+      dlhandle=getPluginHandle(filename);
+   }
+
+   if (!strncmp(cmdtype,"commands",4))
+   {
+      retstr=plugin_commands(dlhandle);
+   }
+   else if (!strcmp(cmdtype,"name"))
+   {
+      retstr=plugin_name(dlhandle);
+   }
+   else if (!strncmp(cmdtype,"desc",4))
+   {
+      retstr=plugin_description(dlhandle);
+   }
+   else
+   {
+      printf_filtered("%s: not a plugin command\n",cmdtype);
+      return;
+   }
+
+   if(retstr!=NULL)
+   {
+      printf_filtered("%s\n",retstr);
+   }
+   else
+   {
+      printf_filtered("command \"%s\" not implemented in plugin %s\n",cmdtype,filename);
+   }
+}
+
+void
+_initialize_plugin(void)
+{
+   struct cmd_list_element *c;
+
+   c = add_cmd ("plugin", class_files, plugin_command,
+      "manage GDB plugins\n\
+a GDB plugin can add functionality to GDB and make use of GDB functionality\n\
+without modifiying the core to GDB.\n\
+load [args]      - loads and initializes a plugin, [ with args ]\n\
+desc[ription]    - print description for file/num\n\
+name             - print name for file/num\n\
+commands         - list commands given by file/num\n\
+search [verbose] - search current plugin-path for gdb plugins [ show desc ]\n\
+list             - list currently loaded plugins\n\
+" ,&cmdlist);
+   set_cmd_completer (c, filename_completer);
+
+   add_show_from_set(add_set_cmd("plugin-path",class_support,
+            var_string_noescape, (char*) &plugin_path,
+            "Set plugin search path\n", &setlist), &showlist);
+
+   pluginList=NULL;
+}
+
diff -uprN ../src-20021028.sparce/gdb/plugin.h gdb/plugin.h
--- ../src-20021028.sparce/gdb/plugin.h	1969-12-31 18:00:00.000000000 -0600
+++ gdb/plugin.h	2002-10-28 13:26:31.000000000 -0600
@@ -0,0 +1,21 @@
+#if !defined (PLUGIN_H)
+#define PLUGIN_H 1
+
+extern void plugin_load(const char *, char *, int);
+extern void plugin_command (char *, int);
+extern void plugin_list(int);
+extern void plugin_search(const char * , int ,int );
+extern const char * plugin_name(void *);
+extern const char * plugin_description(void*);
+extern const char * plugin_commands(void*);
+
+extern char *plugin_path;
+
+struct plugin {
+   void * handle;
+   int num;
+   struct plugin * next;
+   char * filename;
+};
+
+#endif /* !defined (PLUGIN_H) */

[-- Attachment #2: Type: APPLICATION/OCTET-STREAM, Size: 2826 bytes --]

^ permalink raw reply	[flat|nested] 19+ messages in thread

end of thread, other threads:[~2002-11-07 21:10 UTC | newest]

Thread overview: 19+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2002-11-06  7:47 [PATCH] plugin patch Howell, David P
2002-11-06  7:55 ` Daniel Jacobowitz
2002-11-06 11:12 ` Andrew Cagney
2002-11-06 11:55   ` Paul A. Clarke
2002-11-06 12:29     ` Andrew Cagney
  -- strict thread matches above, loose matches on Subject: below --
2002-10-28 14:01 Scott Moser
2002-10-28 22:28 ` Eli Zaretskii
2002-10-29  7:55   ` Paul A. Clarke
2002-11-06  6:45   ` Scott Moser
2002-11-06  6:56     ` Jelmer Vernooij
2002-11-06  7:13       ` Scott Moser
2002-11-06  7:54         ` Jelmer Vernooij
2002-11-06  9:14           ` Andrew Cagney
2002-11-06 15:07             ` Paul A. Clarke
2002-11-06 15:56               ` Andrew Cagney
2002-11-07 10:05                 ` Paul A. Clarke
2002-11-07 11:57                   ` Andrew Cagney
2002-11-07 13:10                   ` Klee Dienes
2002-11-06 15:56 ` Andrew Cagney

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox