/misc/src/release/graphviz-2.18-1/src/graphviz-2.18/lib/gvc/gvplugin.c

Go to the documentation of this file.
00001 /* $Id: gvplugin.c,v 1.56 2008/02/22 14:31:36 glenlow Exp $ $Revision: 1.56 $ */
00002 /* vim:set shiftwidth=4 ts=8: */
00003 
00004 /**********************************************************
00005 *      This software is part of the graphviz package      *
00006 *                http://www.graphviz.org/                 *
00007 *                                                         *
00008 *            Copyright (c) 1994-2004 AT&T Corp.           *
00009 *                and is licensed under the                *
00010 *            Common Public License, Version 1.0           *
00011 *                      by AT&T Corp.                      *
00012 *                                                         *
00013 *        Information and Software Systems Research        *
00014 *              AT&T Research, Florham Park NJ             *
00015 **********************************************************/
00016 
00017 #ifdef HAVE_CONFIG_H
00018 #include "config.h"
00019 #endif
00020 
00021 #include        <string.h>
00022 #ifdef ENABLE_LTDL
00023 #include        <ltdl.h>
00024 #endif
00025 
00026 #include        "memory.h"
00027 #include        "types.h"
00028 #include        "graph.h"
00029 #include        "gvplugin.h"
00030 #include        "gvcjob.h"
00031 #include        "gvcint.h"
00032 #include        "gvcproc.h"
00033 
00034 extern const int Demand_Loading;
00035 
00036 /*
00037  * Define an apis array of name strings using an enumerated api_t as index.
00038  * The enumerated type is defined gvplugin.h.  The apis array is
00039  * inititialized here by redefining ELEM and reinvoking APIS.
00040  */
00041 #define ELEM(x) #x,
00042 static char *api_names[] = { APIS };    /* "render", "layout", ... */
00043 #undef ELEM
00044 
00045 /* translate a string api name to its type, or -1 on error */
00046 api_t gvplugin_api(char *str)
00047 {
00048     int api;
00049 
00050     for (api = 0; api < ARRAY_SIZE(api_names); api++) {
00051         if (strcmp(str, api_names[api]) == 0)
00052             return (api_t)api;
00053     }
00054     return -1;                  /* invalid api */
00055 }
00056 
00057 /* translate api_t into string name, or NULL */
00058 char *gvplugin_api_name(api_t api)
00059 {
00060     if (api < 0 || api >= ARRAY_SIZE(api_names))
00061         return NULL;
00062     return api_names[api];
00063 }
00064 
00065 /* install a plugin description into the list of available plugins
00066  * list is alpha sorted by type (not including :dependency), then
00067  * quality sorted within the type, then, if qualities are the same,
00068  * last install wins.
00069  */
00070 boolean gvplugin_install(GVC_t * gvc, api_t api,
00071                  char *typestr, int quality, char *packagename, char *path,
00072                  gvplugin_installed_t * typeptr)
00073 {
00074     gvplugin_available_t *plugin, **pnext;
00075 #define TYPSIZ 63
00076     char *p, pins[TYPSIZ+1], pnxt[TYPSIZ+1];
00077 
00078     if (api < 0)
00079         return FALSE;
00080 
00081     strncpy(pins, typestr, TYPSIZ);
00082     if ((p = strchr(pins, ':')))
00083         *p = '\0';
00084     
00085     /* point to the beginning of the linked list of plugins for this api */
00086     pnext = &(gvc->apis[api]);
00087 
00088     /* keep alpha-sorted and insert new duplicates ahead of old */
00089     while (*pnext) {
00090         strncpy(pnxt, (*pnext)->typestr, TYPSIZ);
00091         if ((p = strchr(pnxt, ':')))
00092             *p = '\0';
00093         if (strcmp(pins, pnxt) <= 0)
00094             break;
00095         pnext = &((*pnext)->next);
00096     }
00097 
00098     /* keep quality sorted within type and insert new duplicates ahead of old */
00099     while (*pnext) {
00100         strncpy(pnxt, (*pnext)->typestr, TYPSIZ);
00101         if ((p = strchr(pnxt, ':')))
00102             *p = '\0';
00103         if (strcmp(pins, pnxt) != 0)
00104             break;
00105         if (quality >= (*pnext)->quality)
00106             break;
00107         pnext = &((*pnext)->next);
00108     }
00109 
00110     plugin = GNEW(gvplugin_available_t);
00111     plugin->next = *pnext;
00112     *pnext = plugin;
00113     plugin->typestr = typestr;
00114     plugin->quality = quality;
00115     plugin->packagename = packagename;  /* packagename,  all packages */
00116     plugin->path = path;        /* filepath for .so, or NULL for builtins */
00117     plugin->typeptr = typeptr;  /* null if not loaded */
00118 
00119     return TRUE;
00120 }
00121 
00122 /* Activate a plugin description in the list of available plugins.
00123  * This is used when a plugin-library loaded because of demand for
00124  * one of its plugins. It updates the available plugin data with
00125  * pointers into the loaded library.
00126  * NB the quality value is not replaced as it might have been
00127  * manually changed in the config file.
00128  */
00129 static boolean gvplugin_activate(GVC_t * gvc, api_t api,
00130                  char *typestr, char *packagename, char *path,
00131                  gvplugin_installed_t * typeptr)
00132 {
00133     gvplugin_available_t **pnext;
00134 
00135 
00136     if (api < 0)
00137         return FALSE;
00138 
00139     /* point to the beginning of the linked list of plugins for this api */
00140     pnext = &(gvc->apis[api]);
00141 
00142     while (*pnext) {
00143         if ( (strcasecmp(typestr, (*pnext)->typestr) == 0)
00144           && (strcasecmp(packagename, (*pnext)->packagename) == 0)
00145           && (strcasecmp(path, (*pnext)->path) == 0)) {
00146             (*pnext)->typeptr = typeptr;
00147             return TRUE;
00148         }
00149         pnext = &((*pnext)->next);
00150     }
00151     return FALSE;
00152 }
00153 
00154 gvplugin_library_t *gvplugin_library_load(GVC_t *gvc, char *path)
00155 {
00156 #ifdef ENABLE_LTDL
00157     lt_dlhandle hndl;
00158     lt_ptr ptr;
00159     char *s, *sym;
00160     int len;
00161     static char *p;
00162     static int lenp;
00163     char *libdir;
00164     char *suffix = "_LTX_library";
00165 
00166     if (!Demand_Loading)
00167         return NULL;
00168 
00169     libdir = gvconfig_libdir();
00170     len = strlen(libdir) + 1 + strlen(path) + 1;
00171     if (len > lenp) {
00172         lenp = len+20;
00173         if (p)
00174             p = grealloc(p, lenp);
00175         else
00176             p = gmalloc(lenp);
00177     }
00178         
00179 #ifdef WIN32
00180     if (path[1] == ':') {
00181 #else
00182     if (path[0] == '/') {
00183 #endif
00184         strcpy(p, path);
00185     } else {
00186         strcpy(p, libdir);
00187         strcat(p, DIRSEP);
00188         strcat(p, path);
00189     }
00190 
00191     if (lt_dlinit()) {
00192         agerr(AGERR, "failed to init libltdl\n");
00193         return NULL;
00194     }
00195     hndl = lt_dlopen (p);
00196     if (!hndl) {
00197         agerr(AGWARN, "Could not load \"%s\" - %s\n", p, (char*)lt_dlerror());
00198         return NULL;
00199     }
00200     if (gvc->common.verbose >= 2)
00201         fprintf(stderr, "Loading %s\n", p);
00202 
00203         s = strrchr(p, DIRSEP[0]);
00204     len = strlen(s); 
00205 #if defined(WIN32) && !defined(__MINGW32__) && !defined(__CYGWIN__)
00206     if (len < strlen("/gvplugin_x")) {
00207 #else
00208     if (len < strlen("/libgvplugin_x")) {
00209 #endif
00210         agerr (AGERR,"invalid plugin path \"%s\"\n", p);
00211         return NULL;
00212     }
00213     sym = gmalloc(len + strlen(suffix) + 1);
00214 #if defined(WIN32) && !defined(__MINGW32__) && !defined(__CYGWIN__)
00215     strcpy(sym, s+1);         /* strip leading "/"  */
00216 #else
00217     strcpy(sym, s+4);         /* strip leading "/lib" or "/cyg" */
00218 #endif
00219 #if defined(__CYGWIN__) || defined(__MINGW32__)
00220     s = strchr(sym, '-');     /* strip trailing "-1.dll" */
00221 #else 
00222     s = strchr(sym, '.');     /* strip trailing ".so.0" or ".dll" or ".sl" */
00223 #endif
00224     strcpy(s,suffix);         /* append "_LTX_library" */
00225 
00226     ptr = lt_dlsym (hndl, sym);
00227     if (!ptr) {
00228         agerr (AGERR,"failed to resolve %s in %s\n", sym, p);
00229         free(sym);
00230         return NULL;
00231     }
00232     free(sym);
00233     return (gvplugin_library_t *)(ptr);
00234 #else
00235     agerr (AGERR,"dynamic loading not available\n");
00236     return NULL;
00237 #endif
00238 }
00239 
00240 
00241 /* load a plugin of type=str
00242         the str can optionally contain one or more ":dependencies" 
00243 
00244         examples:
00245                 png
00246                 png:cairo
00247         fully qualified:
00248                 png:cairo:cairo
00249                 png:cairo:gd
00250                 png:gd:gd
00251       
00252 */
00253 gvplugin_available_t *gvplugin_load(GVC_t * gvc, api_t api, char *str)
00254 {
00255     gvplugin_available_t **pnext, *rv;
00256     gvplugin_library_t *library;
00257     gvplugin_api_t *apis;
00258     gvplugin_installed_t *types;
00259 #define TYPBUFSIZ 64
00260     char reqtyp[TYPBUFSIZ], typ[TYPBUFSIZ];
00261     char *reqdep, *dep = NULL, *reqpkg;
00262     int i;
00263     api_t apidep;
00264 
00265     /* check for valid apis[] index */
00266     if (api < 0)
00267         return NULL;
00268 
00269     if (api == API_device
00270         || api == API_loadimage) /* api dependencies - FIXME - find better way to code these *s */
00271 
00272         apidep = API_render;    
00273     else
00274         apidep = api;
00275 
00276     strncpy(reqtyp, str, TYPBUFSIZ-1);
00277     reqdep = strchr(reqtyp, ':');
00278     if (reqdep) {
00279         *reqdep++ = '\0';
00280         reqpkg = strchr(reqdep, ':');
00281         if (reqpkg)
00282             *reqpkg++ = '\0';
00283     }
00284     else
00285         reqpkg = NULL;
00286 
00287     /* iterate the linked list of plugins for this api */
00288     for (pnext = &(gvc->apis[api]); *pnext; pnext = &((*pnext)->next)) {
00289         strncpy(typ, (*pnext)->typestr, TYPBUFSIZ-1);
00290         dep = strchr(typ, ':');
00291         if (dep) 
00292             *dep++ = '\0';
00293         if (strcmp(typ, reqtyp)) 
00294             continue;  /* types empty or mismatched */
00295         if (dep && reqdep && strcmp(dep, reqdep))
00296             continue;  /* dependencies not empty, but mismatched */
00297         if (! reqpkg)
00298             break; /* found with no packagename constraints */
00299         if (strcmp(reqpkg, (*pnext)->packagename) == 0)
00300             break;  /* found with required matching packagname */
00301     }
00302     rv = *pnext;
00303 
00304     if (dep && (apidep != api)) /* load dependency if needed */
00305         if (! (gvplugin_load(gvc, apidep, dep)))
00306             rv = NULL;
00307 
00308     if (rv && rv->typeptr == NULL) {
00309         library = gvplugin_library_load(gvc, rv->path);
00310         if (library) {
00311 
00312             /* Now activate the library with real type ptrs */
00313             for (apis = library->apis; (types = apis->types); apis++) {
00314                 for (i = 0; types[i].type; i++) {
00315                     /* NB. quality is not checked or replaced
00316                      *   in case user has manually edited quality in config */
00317                     gvplugin_activate(gvc,
00318                                 apis->api,
00319                                 types[i].type,
00320                                 library->packagename,
00321                                 rv->path,
00322                                 &types[i]);
00323                 }
00324             }
00325             if (gvc->common.verbose >= 1)
00326                 fprintf(stderr, "Activated plugin library: %s\n",
00327                         rv->path ? rv->path : "<builtin>");
00328         }
00329     }
00330 
00331     /* one last check for successfull load */
00332     if (rv && rv->typeptr == NULL)
00333         rv = NULL;
00334 
00335     if (rv && gvc->common.verbose >= 1)
00336         fprintf(stderr, "Using %s: %s:%s\n",
00337                 api_names[api],
00338                 rv->typestr,
00339                 rv->packagename
00340                 );
00341 
00342     gvc->api[api] = rv;
00343     return rv;
00344 }
00345 
00346 /* string buffer management
00347         - FIXME - must have 20 solutions for this same thing */
00348 static const char *append_buf(char sep, char *str, boolean new)
00349 {
00350     static char *buf;
00351     static int bufsz, pos;
00352     int len;
00353     char *p;
00354 
00355     if (new)
00356         pos = 0;
00357     len = strlen(str) + 1;
00358     if (bufsz < (pos + len + 1)) {
00359         bufsz += 4 * len;
00360         buf = grealloc(buf, bufsz);
00361     }
00362     p = buf + pos;
00363     *p++ = sep;
00364     strcpy(p, str);
00365     pos += len;
00366     return buf;
00367 }
00368 
00369 /* assemble a string list of available plugins */
00370 const char *gvplugin_list(GVC_t * gvc, api_t api, char *str)
00371 {
00372     gvplugin_available_t **pnext, **plugin;
00373     const char *buf = NULL;
00374     char *s, *p, *q, *typestr_last;
00375     boolean new = TRUE;
00376 
00377     /* check for valid apis[] index */
00378     if (api < 0)
00379         return NULL;
00380 
00381     /* does str have a :path modifier? */
00382     s = strdup(str);
00383     p = strchr(s, ':');
00384     if (p)
00385         *p++ = '\0';
00386 
00387     /* point to the beginning of the linked list of plugins for this api */
00388     plugin = &(gvc->apis[api]);
00389 
00390     if (p) {    /* if str contains a ':', and if we find a match for the type,
00391                    then just list the alternative paths for the plugin */
00392         for (pnext = plugin; *pnext; pnext = &((*pnext)->next)) {
00393             q = strdup((*pnext)->typestr);
00394             if ((p = strchr(q, ':')))
00395                 *p++ = '\0';
00396             /* list only the matching type, or all types if s is an empty string */
00397             if (!s[0] || strcasecmp(s, q) == 0) {
00398                 /* list each member of the matching type as "type:path" */
00399                 append_buf(' ', (*pnext)->typestr, new);
00400                 buf = append_buf(':', (*pnext)->packagename, FALSE);
00401                 new = FALSE;
00402             }
00403             free(q);
00404         }
00405     }
00406     free(s);
00407     if (new) {                  /* if the type was not found, or if str without ':',
00408                                    then just list available types */
00409         typestr_last = NULL;
00410         for (pnext = plugin; *pnext; pnext = &((*pnext)->next)) {
00411             /* list only one instance of type */
00412             q = strdup((*pnext)->typestr);
00413             if ((p = strchr(q, ':')))
00414                 *p++ = '\0';
00415             if (!typestr_last || strcasecmp(typestr_last, q) != 0) {
00416                 /* list it as "type"  i.e. w/o ":path" */
00417                 buf = append_buf(' ', q, new);
00418                 new = FALSE;
00419             }
00420             if(!typestr_last)
00421                 free(typestr_last);
00422             typestr_last = q;
00423         }
00424         if(!typestr_last)
00425             free(typestr_last);
00426     }
00427     if (!buf)
00428         buf = "";
00429     return buf;
00430 }
00431 
00432 void gvplugin_write_status(GVC_t * gvc)
00433 {
00434     int api;
00435 
00436 #ifdef ENABLE_LTDL
00437     if (Demand_Loading) {
00438         fprintf(stderr,"The plugin configuration file:\n\t%s\n", gvc->config_path);
00439         if (gvc->config_found)
00440             fprintf(stderr,"\t\twas successfully loaded.\n");
00441         else
00442             fprintf(stderr,"\t\twas not found or not usable. No on-demand plugins.\n");
00443     }
00444     else {
00445         fprintf(stderr,"Demand loading of plugins is disabled.\n");
00446     }
00447 #endif
00448 
00449     for (api = 0; api < ARRAY_SIZE(api_names); api++) {
00450         if (gvc->common.verbose >= 2) 
00451             fprintf(stderr,"    %s\t: %s\n", api_names[api], gvplugin_list(gvc, api, ":"));
00452         else
00453             fprintf(stderr,"    %s\t: %s\n", api_names[api], gvplugin_list(gvc, api, "?"));
00454     }
00455 
00456 }

Generated on Mon Mar 31 19:03:26 2008 for Graphviz by  doxygen 1.5.1