Graphviz  2.31.20130520.0446
lib/gvc/gvplugin.c
Go to the documentation of this file.
00001 /* $Id$ $Revision$ */
00002 /* vim:set shiftwidth=4 ts=8: */
00003 
00004 /*************************************************************************
00005  * Copyright (c) 2011 AT&T Intellectual Property 
00006  * All rights reserved. This program and the accompanying materials
00007  * are made available under the terms of the Eclipse Public License v1.0
00008  * which accompanies this distribution, and is available at
00009  * http://www.eclipse.org/legal/epl-v10.html
00010  *
00011  * Contributors: See CVS logs. Details at http://www.graphviz.org/
00012  *************************************************************************/
00013 
00014 #ifdef HAVE_CONFIG_H
00015 #include "config.h"
00016 #endif
00017 
00018 #include        <string.h>
00019 #ifdef ENABLE_LTDL
00020 #include        <ltdl.h>
00021 #endif
00022 
00023 #include        <agxbuf.h>
00024 #include        "memory.h"
00025 #include        "types.h"
00026 #include        "gvplugin.h"
00027 #include        "gvcjob.h"
00028 #include        "gvcint.h"
00029 #include        "gvcproc.h"
00030 #include        "gvio.h"
00031 
00032 #include        "const.h"
00033 
00034 #ifndef HAVE_STRCASECMP
00035 extern int strcasecmp(const char *s1, const char *s2);
00036 #endif
00037 
00038 #ifdef WIN32
00039 #define strdup(x) _strdup(x)
00040 #endif
00041 
00042 /*
00043  * Define an apis array of name strings using an enumerated api_t as index.
00044  * The enumerated type is defined gvplugin.h.  The apis array is
00045  * inititialized here by redefining ELEM and reinvoking APIS.
00046  */
00047 #define ELEM(x) #x,
00048 static char *api_names[] = { APIS };    /* "render", "layout", ... */
00049 #undef ELEM
00050 
00051 /* translate a string api name to its type, or -1 on error */
00052 api_t gvplugin_api(char *str)
00053 {
00054     int api;
00055 
00056     for (api = 0; api < ARRAY_SIZE(api_names); api++) {
00057         if (strcmp(str, api_names[api]) == 0)
00058             return (api_t)api;
00059     }
00060     return -1;                  /* invalid api */
00061 }
00062 
00063 /* translate api_t into string name, or NULL */
00064 char *gvplugin_api_name(api_t api)
00065 {
00066     if (api < 0 || api >= ARRAY_SIZE(api_names))
00067         return NULL;
00068     return api_names[api];
00069 }
00070 
00071 /* install a plugin description into the list of available plugins
00072  * list is alpha sorted by type (not including :dependency), then
00073  * quality sorted within the type, then, if qualities are the same,
00074  * last install wins.
00075  */
00076 boolean gvplugin_install(GVC_t * gvc, api_t api, const char *typestr,
00077         int quality, gvplugin_package_t *package,
00078         gvplugin_installed_t * typeptr)
00079 {
00080     gvplugin_available_t *plugin, **pnext;
00081 #define TYPSIZ 63
00082     char *p, pins[TYPSIZ+1], pnxt[TYPSIZ+1];
00083 
00084     if (api < 0)
00085         return FALSE;
00086 
00087     strncpy(pins, typestr, TYPSIZ);
00088     if ((p = strchr(pins, ':')))
00089         *p = '\0';
00090     
00091     /* point to the beginning of the linked list of plugins for this api */
00092     pnext = &(gvc->apis[api]);
00093 
00094     /* keep alpha-sorted and insert new duplicates ahead of old */
00095     while (*pnext) {
00096         strncpy(pnxt, (*pnext)->typestr, TYPSIZ);
00097         if ((p = strchr(pnxt, ':')))
00098             *p = '\0';
00099         if (strcmp(pins, pnxt) <= 0)
00100             break;
00101         pnext = &((*pnext)->next);
00102     }
00103 
00104     /* keep quality sorted within type and insert new duplicates ahead of old */
00105     while (*pnext) {
00106         strncpy(pnxt, (*pnext)->typestr, TYPSIZ);
00107         if ((p = strchr(pnxt, ':')))
00108             *p = '\0';
00109         if (strcmp(pins, pnxt) != 0)
00110             break;
00111         if (quality >= (*pnext)->quality)
00112             break;
00113         pnext = &((*pnext)->next);
00114     }
00115 
00116     plugin = GNEW(gvplugin_available_t);
00117     plugin->next = *pnext;
00118     *pnext = plugin;
00119     plugin->typestr = typestr;
00120     plugin->quality = quality;
00121     plugin->package = package;
00122     plugin->typeptr = typeptr;  /* null if not loaded */
00123 
00124     return TRUE;
00125 }
00126 
00127 /* Activate a plugin description in the list of available plugins.
00128  * This is used when a plugin-library loaded because of demand for
00129  * one of its plugins. It updates the available plugin data with
00130  * pointers into the loaded library.
00131  * NB the quality value is not replaced as it might have been
00132  * manually changed in the config file.
00133  */
00134 static boolean gvplugin_activate(GVC_t * gvc, api_t api,
00135                  const char *typestr, char *name, char *path,
00136                  gvplugin_installed_t * typeptr)
00137 {
00138     gvplugin_available_t **pnext;
00139 
00140 
00141     if (api < 0)
00142         return FALSE;
00143 
00144     /* point to the beginning of the linked list of plugins for this api */
00145     pnext = &(gvc->apis[api]);
00146 
00147     while (*pnext) {
00148         if ( (strcasecmp(typestr, (*pnext)->typestr) == 0)
00149           && (strcasecmp(name, (*pnext)->package->name) == 0)
00150           && ((*pnext)->package->path != 0)
00151           && (strcasecmp(path, (*pnext)->package->path) == 0)) {
00152             (*pnext)->typeptr = typeptr;
00153             return TRUE;
00154         }
00155         pnext = &((*pnext)->next);
00156     }
00157     return FALSE;
00158 }
00159 
00160 gvplugin_library_t *gvplugin_library_load(GVC_t *gvc, char *path)
00161 {
00162 #ifdef ENABLE_LTDL
00163     lt_dlhandle hndl;
00164     lt_ptr ptr;
00165     char *s, *sym;
00166     int len;
00167     static char *p;
00168     static int lenp;
00169     char *libdir;
00170     char *suffix = "_LTX_library";
00171 
00172     if (! gvc->common.demand_loading)
00173         return NULL;
00174 
00175     libdir = gvconfig_libdir(gvc);
00176     len = strlen(libdir) + 1 + strlen(path) + 1;
00177     if (len > lenp) {
00178         lenp = len+20;
00179         if (p)
00180             p = grealloc(p, lenp);
00181         else
00182             p = gmalloc(lenp);
00183     }
00184         
00185 #ifdef WIN32
00186     if (path[1] == ':') {
00187 #else
00188     if (path[0] == '/') {
00189 #endif
00190         strcpy(p, path);
00191     } else {
00192         strcpy(p, libdir);
00193         strcat(p, DIRSEP);
00194         strcat(p, path);
00195     }
00196 
00197     if (lt_dlinit()) {
00198         agerr(AGERR, "failed to init libltdl\n");
00199         return NULL;
00200     }
00201     hndl = lt_dlopen (p);
00202     if (!hndl) {
00203         agerr(AGWARN, "Could not load \"%s\" - %s\n", p, (char*)lt_dlerror());
00204         return NULL;
00205     }
00206     if (gvc->common.verbose >= 2)
00207         fprintf(stderr, "Loading %s\n", p);
00208 
00209         s = strrchr(p, DIRSEP[0]);
00210     len = strlen(s); 
00211 #if defined(WIN32) && !defined(__MINGW32__) && !defined(__CYGWIN__)
00212     if (len < strlen("/gvplugin_x")) {
00213 #else
00214     if (len < strlen("/libgvplugin_x")) {
00215 #endif
00216         agerr (AGERR,"invalid plugin path \"%s\"\n", p);
00217         return NULL;
00218     }
00219     sym = gmalloc(len + strlen(suffix) + 1);
00220 #if defined(WIN32) && !defined(__MINGW32__) && !defined(__CYGWIN__)
00221     strcpy(sym, s+1);         /* strip leading "/"  */
00222 #else
00223     strcpy(sym, s+4);         /* strip leading "/lib" or "/cyg" */
00224 #endif
00225 #if defined(__CYGWIN__) || defined(__MINGW32__)
00226     s = strchr(sym, '-');     /* strip trailing "-1.dll" */
00227 #else 
00228     s = strchr(sym, '.');     /* strip trailing ".so.0" or ".dll" or ".sl" */
00229 #endif
00230     strcpy(s,suffix);         /* append "_LTX_library" */
00231 
00232     ptr = lt_dlsym (hndl, sym);
00233     if (!ptr) {
00234         agerr (AGERR,"failed to resolve %s in %s\n", sym, p);
00235         free(sym);
00236         return NULL;
00237     }
00238     free(sym);
00239     return (gvplugin_library_t *)(ptr);
00240 #else
00241     agerr (AGERR,"dynamic loading not available\n");
00242     return NULL;
00243 #endif
00244 }
00245 
00246 
00247 /* load a plugin of type=str
00248         the str can optionally contain one or more ":dependencies" 
00249 
00250         examples:
00251                 png
00252                 png:cairo
00253         fully qualified:
00254                 png:cairo:cairo
00255                 png:cairo:gd
00256                 png:gd:gd
00257       
00258 */
00259 gvplugin_available_t *gvplugin_load(GVC_t * gvc, api_t api, const char *str)
00260 {
00261     gvplugin_available_t **pnext, *rv;
00262     gvplugin_library_t *library;
00263     gvplugin_api_t *apis;
00264     gvplugin_installed_t *types;
00265 #define TYPBUFSIZ 64
00266     char reqtyp[TYPBUFSIZ], typ[TYPBUFSIZ];
00267     char *reqdep, *dep = NULL, *reqpkg;
00268     int i;
00269     api_t apidep;
00270 
00271     /* check for valid apis[] index */
00272     if (api < 0)
00273         return NULL;
00274 
00275     if (api == API_device
00276         || api == API_loadimage) /* api dependencies - FIXME - find better way to code these *s */
00277 
00278         apidep = API_render;    
00279     else
00280         apidep = api;
00281 
00282     strncpy(reqtyp, str, TYPBUFSIZ-1);
00283     reqdep = strchr(reqtyp, ':');
00284     if (reqdep) {
00285         *reqdep++ = '\0';
00286         reqpkg = strchr(reqdep, ':');
00287         if (reqpkg)
00288             *reqpkg++ = '\0';
00289     }
00290     else
00291         reqpkg = NULL;
00292 
00293     /* iterate the linked list of plugins for this api */
00294     for (pnext = &(gvc->apis[api]); *pnext; pnext = &((*pnext)->next)) {
00295         strncpy(typ, (*pnext)->typestr, TYPBUFSIZ-1);
00296         dep = strchr(typ, ':');
00297         if (dep) 
00298             *dep++ = '\0';
00299         if (strcmp(typ, reqtyp)) 
00300             continue;  /* types empty or mismatched */
00301         if (dep && reqdep && strcmp(dep, reqdep))
00302             continue;  /* dependencies not empty, but mismatched */
00303         if (! reqpkg || strcmp(reqpkg, (*pnext)->package->name) == 0)
00304         {
00305                 /* found with no packagename constraints, or with required matching packagname */
00306     
00307                 if (dep && (apidep != api)) /* load dependency if needed, continue if can't find */
00308                         if (! (gvplugin_load(gvc, apidep, dep)))
00309                                 continue;
00310                 break;
00311     }
00312     }
00313     rv = *pnext;
00314 
00315     if (rv && rv->typeptr == NULL) {
00316         library = gvplugin_library_load(gvc, rv->package->path);
00317         if (library) {
00318 
00319             /* Now activate the library with real type ptrs */
00320             for (apis = library->apis; (types = apis->types); apis++) {
00321                 for (i = 0; types[i].type; i++) {
00322                     /* NB. quality is not checked or replaced
00323                      *   in case user has manually edited quality in config */
00324                     gvplugin_activate(gvc,
00325                                 apis->api,
00326                                 types[i].type,
00327                                 library->packagename,
00328                                 rv->package->path,
00329                                 &types[i]);
00330                 }
00331             }
00332             if (gvc->common.verbose >= 1)
00333                 fprintf(stderr, "Activated plugin library: %s\n",
00334                         rv->package->path ? rv->package->path : "<builtin>");
00335         }
00336     }
00337 
00338     /* one last check for successfull load */
00339     if (rv && rv->typeptr == NULL)
00340         rv = NULL;
00341 
00342     if (rv && gvc->common.verbose >= 1)
00343         fprintf(stderr, "Using %s: %s:%s\n",
00344                 api_names[api],
00345                 rv->typestr,
00346                 rv->package->name
00347                 );
00348 
00349     gvc->api[api] = rv;
00350     return rv;
00351 }
00352 
00353 /* assemble a string list of available plugins 
00354  * non-re-entrant as character store is shared
00355  */
00356 char *gvplugin_list(GVC_t * gvc, api_t api, const char *str)
00357 {
00358     static int first = 1;
00359     gvplugin_available_t **pnext, **plugin;
00360     char *bp;
00361     char *s, *p, *q, *typestr_last;
00362     boolean new = TRUE;
00363     static agxbuf xb;
00364 
00365     /* check for valid apis[] index */
00366     if (api < 0)
00367         return NULL;
00368 
00369     /* check for valid str */
00370     if (! str)
00371         return NULL;
00372 
00373     if (first) {
00374         agxbinit(&xb, 0, 0);
00375         first = 0;
00376     }
00377 
00378     /* does str have a :path modifier? */
00379     s = strdup(str);
00380     p = strchr(s, ':');
00381     if (p)
00382         *p++ = '\0';
00383 
00384     /* point to the beginning of the linked list of plugins for this api */
00385     plugin = &(gvc->apis[api]);
00386 
00387     if (p) {    /* if str contains a ':', and if we find a match for the type,
00388                    then just list the alternative paths for the plugin */
00389         for (pnext = plugin; *pnext; pnext = &((*pnext)->next)) {
00390             q = strdup((*pnext)->typestr);
00391             if ((p = strchr(q, ':')))
00392                 *p++ = '\0';
00393             /* list only the matching type, or all types if s is an empty string */
00394             if (!s[0] || strcasecmp(s, q) == 0) {
00395                 /* list each member of the matching type as "type:path" */
00396                 agxbputc(&xb,' '); agxbput(&xb, (*pnext)->typestr);
00397                 agxbputc(&xb,':'); agxbput(&xb, (*pnext)->package->name);
00398                 new = FALSE;
00399             }
00400             free(q);
00401         }
00402     }
00403     free(s);
00404     if (new) {                  /* if the type was not found, or if str without ':',
00405                                    then just list available types */
00406         typestr_last = NULL;
00407         for (pnext = plugin; *pnext; pnext = &((*pnext)->next)) {
00408             /* list only one instance of type */
00409             q = strdup((*pnext)->typestr);
00410             if ((p = strchr(q, ':')))
00411                 *p++ = '\0';
00412             if (!typestr_last || strcasecmp(typestr_last, q) != 0) {
00413                 /* list it as "type"  i.e. w/o ":path" */
00414                 agxbputc(&xb,' '); agxbput(&xb, q);
00415                 new = FALSE;
00416             }
00417             if(!typestr_last)
00418                 free(typestr_last);
00419             typestr_last = q;
00420         }
00421         if(!typestr_last)
00422             free(typestr_last);
00423     }
00424     if (new)
00425         bp = "";
00426     else
00427         bp = agxbuse(&xb);
00428     return bp;
00429 }
00430 
00431 /* gvPluginList:
00432  * Return list of plugins of type kind.
00433  * The size of the list is stored in sz.
00434  * The caller is responsible for freeing the storage. This involves
00435  * freeing each item, then the list.
00436  * Returns NULL on error, or if there are no plugins.
00437  * In the former case, sz is unchanged; in the latter, sz = 0.
00438  *
00439  * At present, the str argument is unused, but may be used to modify
00440  * the search as in gvplugin_list above.
00441  */
00442 char **gvPluginList(GVC_t * gvc, char* kind, int* sz, const char *str)
00443 {
00444     int api;
00445     gvplugin_available_t **pnext, **plugin;
00446     int cnt = 0;    
00447     char** list = NULL;
00448     char *p, *q, *typestr_last;
00449 
00450     if (!kind) return NULL;
00451     for (api = 0; api < ARRAY_SIZE(api_names); api++) {
00452         if (!strcasecmp(kind,api_names[api]))
00453             break;
00454     }
00455     if (api == ARRAY_SIZE(api_names)) {
00456         agerr(AGERR, "unrecognized api name \"%s\"\n", kind);
00457         return NULL;
00458     }
00459 
00460     /* point to the beginning of the linked list of plugins for this api */
00461     plugin = &(gvc->apis[api]);
00462     typestr_last = NULL;
00463     for (pnext = plugin; *pnext; pnext = &((*pnext)->next)) {
00464             /* list only one instance of type */
00465         q = strdup((*pnext)->typestr);
00466         if ((p = strchr(q, ':')))
00467             *p++ = '\0';
00468         if (!typestr_last || strcasecmp(typestr_last, q) != 0) {
00469             list = RALLOC(cnt+1,list,char*);
00470             list[cnt++] = q;
00471         }
00472         typestr_last = q;
00473     }
00474 
00475     *sz = cnt;
00476     return list;
00477 }
00478 
00479 void gvplugin_write_status(GVC_t * gvc)
00480 {
00481     int api;
00482 
00483 #ifdef ENABLE_LTDL
00484     if (gvc->common.demand_loading) {
00485         fprintf(stderr,"The plugin configuration file:\n\t%s\n", gvc->config_path);
00486         if (gvc->config_found)
00487             fprintf(stderr,"\t\twas successfully loaded.\n");
00488         else
00489             fprintf(stderr,"\t\twas not found or not usable. No on-demand plugins.\n");
00490     }
00491     else {
00492         fprintf(stderr,"Demand loading of plugins is disabled.\n");
00493     }
00494 #endif
00495 
00496     for (api = 0; api < ARRAY_SIZE(api_names); api++) {
00497         if (gvc->common.verbose >= 2) 
00498             fprintf(stderr,"    %s\t: %s\n", api_names[api], gvplugin_list(gvc, api, ":"));
00499         else
00500             fprintf(stderr,"    %s\t: %s\n", api_names[api], gvplugin_list(gvc, api, "?"));
00501     }
00502 
00503 }
00504 
00505 Agraph_t * gvplugin_graph(GVC_t * gvc)
00506 {
00507     Agraph_t *g, *sg, *ssg;
00508     Agnode_t *n, *m;
00509     Agedge_t *e;
00510     Agsym_t *a;
00511     gvplugin_package_t *package;
00512     gvplugin_available_t **pnext;
00513     char bufa[100], *buf1, *buf2, bufb[100], *p, *q, *t;
00514     int api, found;
00515 
00516 #ifndef WITH_CGRAPH
00517     aginit();
00518     agsetiodisc(NULL, gvfwrite, gvferror);
00519     /* set persistent attributes here */
00520     agraphattr(NULL, "label", "");
00521     agraphattr(NULL, "rankdir", "");
00522     agraphattr(NULL, "rank", "");
00523     agraphattr(NULL, "ranksep", "");
00524     agnodeattr(NULL, "label", NODENAME_ESC);
00525 
00526     g = agopen("G", AGDIGRAPH);
00527 #else
00528     g = agopen("G", Agdirected, NIL(Agdisc_t *));
00529     agattr(g, AGRAPH, "label", "");
00530     agattr(g, AGRAPH, "rankdir", "");
00531     agattr(g, AGRAPH, "rank", "");
00532     agattr(g, AGRAPH, "ranksep", "");
00533     agattr(g, AGNODE, "label", NODENAME_ESC);
00534 #endif
00535 
00536     a = agfindgraphattr(g, "rankdir");
00537 #ifndef WITH_CGRAPH
00538     agxset(g, a->index, "LR");
00539 #else
00540     agxset(g, a, "LR");
00541 #endif
00542 
00543     a = agfindgraphattr(g, "ranksep");
00544 #ifndef WITH_CGRAPH
00545     agxset(g, a->index, "1.5");
00546 #else
00547     agxset(g, a, "1.5");
00548 #endif
00549 
00550     a = agfindgraphattr(g, "label");
00551 #ifndef WITH_CGRAPH
00552     agxset(g, a->index, "Plugins");
00553 #else
00554     agxset(g, a, "Plugins");
00555 #endif
00556 
00557     for (package = gvc->packages; package; package = package->next) {
00558         strcpy(bufa, "cluster_");
00559         strcat(bufa, package->name); 
00560 #ifndef WITH_CGRAPH
00561         sg = agsubg(g, bufa);
00562 #else
00563         sg = agsubg(g, bufa, 1);
00564 #endif
00565         a = agfindgraphattr(sg, "label");
00566 #ifndef WITH_CGRAPH
00567         agxset(sg, a->index, package->name);
00568 #else
00569         agxset(sg, a, package->name);
00570 #endif
00571         strcpy(bufa, package->name); 
00572         strcat(bufa, "_");
00573         buf1 = bufa + strlen(bufa);
00574         for (api = 0; api < ARRAY_SIZE(api_names); api++) {
00575             found = 0;
00576             strcpy(buf1, api_names[api]);
00577 #ifndef WITH_CGRAPH
00578             ssg = agsubg(sg, bufa);
00579 #else
00580             ssg = agsubg(sg, bufa, 1);
00581 #endif
00582             a = agfindgraphattr(ssg, "rank");
00583 #ifndef WITH_CGRAPH
00584             agxset(ssg, a->index, "same");
00585 #else
00586             agxset(ssg, a, "same");
00587 #endif
00588             strcat(buf1, "_");
00589             buf2 = bufa + strlen(bufa);
00590             for (pnext = &(gvc->apis[api]); *pnext; pnext = &((*pnext)->next)) {
00591                 if ((*pnext)->package == package) {
00592                     found++;
00593                     t = q = strdup((*pnext)->typestr);
00594                     if ((p = strchr(q, ':'))) *p++ = '\0';
00595                     /* Now p = renderer, e.g. "gd"
00596                      * and q = device, e.g. "png"
00597                      * or  q = imageloader, e.g. "png" */
00598                     switch (api) {
00599                     case API_device:
00600                     case API_loadimage:
00601 
00602                         /* hack for aliases */
00603                         if (!strncmp(q,"jp",2))
00604                             q = "jpeg/jpe/jpg";
00605                         else if (!strncmp(q,"tif",3))
00606                             q = "tiff/tif";
00607                         else if (!strcmp(q,"x11") || !strcmp(q,"xlib"))
00608                             q = "x11/xlib";
00609                         else if (!strcmp(q,"dot") || !strcmp(q,"gv"))
00610                             q = "gv/dot";
00611 
00612                         strcpy(buf2, q);
00613 #ifndef WITH_CGRAPH
00614                         n = agnode(ssg, bufa);
00615 #else
00616                         n = agnode(ssg, bufa, 1);
00617 #endif
00618                         a = agfindnodeattr(g, "label");
00619 #ifndef WITH_CGRAPH
00620                         agxset(n, a->index, q);
00621 #else
00622                         agxset(n, a, q);
00623 #endif
00624                         if (! (p && *p)) {
00625                             strcpy(bufb, "render_cg");
00626                             m = agfindnode(sg, bufb);
00627                             if (!m) {
00628 #ifndef WITH_CGRAPH
00629                                 m = agnode(sg, bufb);
00630 #else
00631                                 m = agnode(sg, bufb, 1);
00632 #endif
00633                                 a = agfindgraphattr(g, "label");
00634 #ifndef WITH_CGRAPH
00635                                 agxset(m, a->index, "cg");
00636 #else
00637                                 agxset(m, a, "cg");
00638 #endif
00639                             }
00640 #ifndef WITH_CGRAPH
00641                             agedge(sg, m, n);
00642 #else
00643                             agedge(sg, m, n, NULL, 1);
00644 #endif
00645                         }
00646                         break;
00647                     case API_render:
00648                         strcpy(bufb, api_names[api]);
00649                         strcat(bufb, "_");
00650                         strcat(bufb, q);
00651 #ifndef WITH_CGRAPH
00652                         n = agnode(ssg, bufb);
00653 #else
00654                         n = agnode(ssg, bufb, 1);
00655 #endif
00656                         a = agfindnodeattr(g, "label");
00657 #ifndef WITH_CGRAPH
00658                         agxset(n, a->index, q);
00659 #else
00660                         agxset(n, a, q);
00661 #endif
00662                         break;
00663                     default:
00664                         break;
00665                     }
00666                     free(t);
00667                 }
00668             }
00669             if (!found)
00670 #ifndef WITH_CGRAPH
00671                 agdelete(ssg->meta_node->graph, ssg->meta_node);
00672 #else
00673                 agdelete(g, ssg);
00674 #endif
00675         }
00676     }
00677 
00678 #ifndef WITH_CGRAPH
00679     ssg = agsubg(g, "output_formats");
00680 #else
00681     ssg = agsubg(g, "output_formats", 1);
00682 #endif
00683     a = agfindgraphattr(ssg, "rank");
00684 #ifndef WITH_CGRAPH
00685     agxset(ssg, a->index, "same");
00686 #else
00687     agxset(ssg, a, "same");
00688 #endif
00689     for (package = gvc->packages; package; package = package->next) {
00690         strcpy(bufa, package->name); 
00691         strcat(bufa, "_");
00692         buf1 = bufa + strlen(bufa);
00693         for (api = 0; api < ARRAY_SIZE(api_names); api++) {
00694             strcpy(buf1, api_names[api]);
00695             strcat(buf1, "_");
00696             buf2 = bufa + strlen(bufa);
00697             for (pnext = &(gvc->apis[api]); *pnext; pnext = &((*pnext)->next)) {
00698                 if ((*pnext)->package == package) {
00699                     t = q = strdup((*pnext)->typestr);
00700                     if ((p = strchr(q, ':'))) *p++ = '\0';
00701                     /* Now p = renderer, e.g. "gd"
00702                      * and q = device, e.g. "png"
00703                      * or  q = imageloader, e.g. "png" */
00704 
00705                     /* hack for aliases */
00706                     if (!strncmp(q,"jp",2))
00707                         q = "jpeg/jpe/jpg";
00708                     else if (!strncmp(q,"tif",3))
00709                         q = "tiff/tif";
00710                     else if (!strcmp(q,"x11") || !strcmp(q,"xlib"))
00711                         q = "x11/xlib";
00712                     else if (!strcmp(q,"dot") || !strcmp(q,"gv"))
00713                         q = "gv/dot";
00714 
00715                     switch (api) {
00716                     case API_device:
00717                         strcpy(buf2, q);
00718 #ifndef WITH_CGRAPH
00719                         n = agnode(g, bufa);
00720 #else
00721                         n = agnode(g, bufa, 1);
00722 #endif
00723                         strcpy(bufb, "output_");
00724                         strcat(bufb, q);
00725                         m = agfindnode(ssg, bufb);
00726                         if (!m) {
00727 #ifndef WITH_CGRAPH
00728                             m = agnode(ssg, bufb);
00729 #else
00730                             m = agnode(ssg, bufb, 1);
00731 #endif
00732                             a = agfindnodeattr(g, "label");
00733 #ifndef WITH_CGRAPH
00734                             agxset(m, a->index, q);
00735 #else
00736                             agxset(m, a, q);
00737 #endif
00738                         }
00739                         e = agfindedge(g, n, m);
00740                         if (!e)
00741 #ifndef WITH_CGRAPH
00742                             e = agedge(g, n, m);
00743 #else
00744                             e = agedge(g, n, m, NULL, 1);
00745 #endif
00746                         if (p && *p) {
00747                             strcpy(bufb, "render_");
00748                             strcat(bufb, p);
00749                             m = agfindnode(ssg, bufb);
00750                             if (!m)
00751 #ifndef WITH_CGRAPH
00752                                 m = agnode(g, bufb);
00753 #else
00754                                 m = agnode(g, bufb, 1);
00755 #endif
00756                             e = agfindedge(g, m, n);
00757                             if (!e)
00758 #ifndef WITH_CGRAPH
00759                                 e = agedge(g, m, n);
00760 #else
00761                                 e = agedge(g, m, n, NULL, 1);
00762 #endif
00763                         }
00764                         break;
00765                     case API_loadimage:
00766                         strcpy(buf2, q);
00767 #ifndef WITH_CGRAPH
00768                         n = agnode(g, bufa);
00769 #else
00770                         n = agnode(g, bufa, 1);
00771 #endif
00772                         strcpy(bufb, "input_");
00773                         strcat(bufb, q);
00774                         m = agfindnode(g, bufb);
00775                         if (!m) {
00776 #ifndef WITH_CGRAPH
00777                             m = agnode(g, bufb);
00778 #else
00779                             m = agnode(g, bufb, 1);
00780 #endif
00781                             a = agfindnodeattr(g, "label");
00782 #ifndef WITH_CGRAPH
00783                             agxset(m, a->index, q);
00784 #else
00785                             agxset(m, a, q);
00786 #endif
00787                         }
00788                         e = agfindedge(g, m, n);
00789                         if (!e)
00790 #ifndef WITH_CGRAPH
00791                             e = agedge(g, m, n);
00792 #else
00793                             e = agedge(g, m, n, NULL, 1);
00794 #endif
00795                         strcpy(bufb, "render_");
00796                         strcat(bufb, p);
00797                         m = agfindnode(g, bufb);
00798                         if (!m)
00799 #ifndef WITH_CGRAPH
00800                             m = agnode(g, bufb); 
00801 #else
00802                             m = agnode(g, bufb, 1); 
00803 #endif
00804                         e = agfindedge(g, n, m);
00805                         if (!e)
00806 #ifndef WITH_CGRAPH
00807                             e = agedge(g, n, m);
00808 #else
00809                             e = agedge(g, n, m, NULL, 1);
00810 #endif
00811                         break;
00812                     default:
00813                         break;
00814                     }
00815                     free(t);
00816                 }
00817             }
00818         }
00819     }
00820 
00821     return g;
00822 }