Graphviz  2.29.20120524.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 /*
00039  * Define an apis array of name strings using an enumerated api_t as index.
00040  * The enumerated type is defined gvplugin.h.  The apis array is
00041  * inititialized here by redefining ELEM and reinvoking APIS.
00042  */
00043 #define ELEM(x) #x,
00044 static char *api_names[] = { APIS };    /* "render", "layout", ... */
00045 #undef ELEM
00046 
00047 /* translate a string api name to its type, or -1 on error */
00048 api_t gvplugin_api(char *str)
00049 {
00050     int api;
00051 
00052     for (api = 0; api < ARRAY_SIZE(api_names); api++) {
00053         if (strcmp(str, api_names[api]) == 0)
00054             return (api_t)api;
00055     }
00056     return -1;                  /* invalid api */
00057 }
00058 
00059 /* translate api_t into string name, or NULL */
00060 char *gvplugin_api_name(api_t api)
00061 {
00062     if (api < 0 || api >= ARRAY_SIZE(api_names))
00063         return NULL;
00064     return api_names[api];
00065 }
00066 
00067 /* install a plugin description into the list of available plugins
00068  * list is alpha sorted by type (not including :dependency), then
00069  * quality sorted within the type, then, if qualities are the same,
00070  * last install wins.
00071  */
00072 boolean gvplugin_install(GVC_t * gvc, api_t api, const char *typestr,
00073         int quality, gvplugin_package_t *package,
00074         gvplugin_installed_t * typeptr)
00075 {
00076     gvplugin_available_t *plugin, **pnext;
00077 #define TYPSIZ 63
00078     char *p, pins[TYPSIZ+1], pnxt[TYPSIZ+1];
00079 
00080     if (api < 0)
00081         return FALSE;
00082 
00083     strncpy(pins, typestr, TYPSIZ);
00084     if ((p = strchr(pins, ':')))
00085         *p = '\0';
00086     
00087     /* point to the beginning of the linked list of plugins for this api */
00088     pnext = &(gvc->apis[api]);
00089 
00090     /* keep alpha-sorted and insert new duplicates ahead of old */
00091     while (*pnext) {
00092         strncpy(pnxt, (*pnext)->typestr, TYPSIZ);
00093         if ((p = strchr(pnxt, ':')))
00094             *p = '\0';
00095         if (strcmp(pins, pnxt) <= 0)
00096             break;
00097         pnext = &((*pnext)->next);
00098     }
00099 
00100     /* keep quality sorted within type and insert new duplicates ahead of old */
00101     while (*pnext) {
00102         strncpy(pnxt, (*pnext)->typestr, TYPSIZ);
00103         if ((p = strchr(pnxt, ':')))
00104             *p = '\0';
00105         if (strcmp(pins, pnxt) != 0)
00106             break;
00107         if (quality >= (*pnext)->quality)
00108             break;
00109         pnext = &((*pnext)->next);
00110     }
00111 
00112     plugin = GNEW(gvplugin_available_t);
00113     plugin->next = *pnext;
00114     *pnext = plugin;
00115     plugin->typestr = typestr;
00116     plugin->quality = quality;
00117     plugin->package = package;
00118     plugin->typeptr = typeptr;  /* null if not loaded */
00119 
00120     return TRUE;
00121 }
00122 
00123 /* Activate a plugin description in the list of available plugins.
00124  * This is used when a plugin-library loaded because of demand for
00125  * one of its plugins. It updates the available plugin data with
00126  * pointers into the loaded library.
00127  * NB the quality value is not replaced as it might have been
00128  * manually changed in the config file.
00129  */
00130 static boolean gvplugin_activate(GVC_t * gvc, api_t api,
00131                  const char *typestr, char *name, char *path,
00132                  gvplugin_installed_t * typeptr)
00133 {
00134     gvplugin_available_t **pnext;
00135 
00136 
00137     if (api < 0)
00138         return FALSE;
00139 
00140     /* point to the beginning of the linked list of plugins for this api */
00141     pnext = &(gvc->apis[api]);
00142 
00143     while (*pnext) {
00144         if ( (strcasecmp(typestr, (*pnext)->typestr) == 0)
00145           && (strcasecmp(name, (*pnext)->package->name) == 0)
00146           && ((*pnext)->package->path != 0)
00147           && (strcasecmp(path, (*pnext)->package->path) == 0)) {
00148             (*pnext)->typeptr = typeptr;
00149             return TRUE;
00150         }
00151         pnext = &((*pnext)->next);
00152     }
00153     return FALSE;
00154 }
00155 
00156 gvplugin_library_t *gvplugin_library_load(GVC_t *gvc, char *path)
00157 {
00158 #ifdef ENABLE_LTDL
00159     lt_dlhandle hndl;
00160     lt_ptr ptr;
00161     char *s, *sym;
00162     int len;
00163     static char *p;
00164     static int lenp;
00165     char *libdir;
00166     char *suffix = "_LTX_library";
00167 
00168     if (! gvc->common.demand_loading)
00169         return NULL;
00170 
00171     libdir = gvconfig_libdir(gvc);
00172     len = strlen(libdir) + 1 + strlen(path) + 1;
00173     if (len > lenp) {
00174         lenp = len+20;
00175         if (p)
00176             p = grealloc(p, lenp);
00177         else
00178             p = gmalloc(lenp);
00179     }
00180         
00181 #ifdef WIN32
00182     if (path[1] == ':') {
00183 #else
00184     if (path[0] == '/') {
00185 #endif
00186         strcpy(p, path);
00187     } else {
00188         strcpy(p, libdir);
00189         strcat(p, DIRSEP);
00190         strcat(p, path);
00191     }
00192 
00193     if (lt_dlinit()) {
00194         agerr(AGERR, "failed to init libltdl\n");
00195         return NULL;
00196     }
00197     hndl = lt_dlopen (p);
00198     if (!hndl) {
00199         agerr(AGWARN, "Could not load \"%s\" - %s\n", p, (char*)lt_dlerror());
00200         return NULL;
00201     }
00202     if (gvc->common.verbose >= 2)
00203         fprintf(stderr, "Loading %s\n", p);
00204 
00205         s = strrchr(p, DIRSEP[0]);
00206     len = strlen(s); 
00207 #if defined(WIN32) && !defined(__MINGW32__) && !defined(__CYGWIN__)
00208     if (len < strlen("/gvplugin_x")) {
00209 #else
00210     if (len < strlen("/libgvplugin_x")) {
00211 #endif
00212         agerr (AGERR,"invalid plugin path \"%s\"\n", p);
00213         return NULL;
00214     }
00215     sym = gmalloc(len + strlen(suffix) + 1);
00216 #if defined(WIN32) && !defined(__MINGW32__) && !defined(__CYGWIN__)
00217     strcpy(sym, s+1);         /* strip leading "/"  */
00218 #else
00219     strcpy(sym, s+4);         /* strip leading "/lib" or "/cyg" */
00220 #endif
00221 #if defined(__CYGWIN__) || defined(__MINGW32__)
00222     s = strchr(sym, '-');     /* strip trailing "-1.dll" */
00223 #else 
00224     s = strchr(sym, '.');     /* strip trailing ".so.0" or ".dll" or ".sl" */
00225 #endif
00226     strcpy(s,suffix);         /* append "_LTX_library" */
00227 
00228     ptr = lt_dlsym (hndl, sym);
00229     if (!ptr) {
00230         agerr (AGERR,"failed to resolve %s in %s\n", sym, p);
00231         free(sym);
00232         return NULL;
00233     }
00234     free(sym);
00235     return (gvplugin_library_t *)(ptr);
00236 #else
00237     agerr (AGERR,"dynamic loading not available\n");
00238     return NULL;
00239 #endif
00240 }
00241 
00242 
00243 /* load a plugin of type=str
00244         the str can optionally contain one or more ":dependencies" 
00245 
00246         examples:
00247                 png
00248                 png:cairo
00249         fully qualified:
00250                 png:cairo:cairo
00251                 png:cairo:gd
00252                 png:gd:gd
00253       
00254 */
00255 gvplugin_available_t *gvplugin_load(GVC_t * gvc, api_t api, const char *str)
00256 {
00257     gvplugin_available_t **pnext, *rv;
00258     gvplugin_library_t *library;
00259     gvplugin_api_t *apis;
00260     gvplugin_installed_t *types;
00261 #define TYPBUFSIZ 64
00262     char reqtyp[TYPBUFSIZ], typ[TYPBUFSIZ];
00263     char *reqdep, *dep = NULL, *reqpkg;
00264     int i;
00265     api_t apidep;
00266 
00267     /* check for valid apis[] index */
00268     if (api < 0)
00269         return NULL;
00270 
00271     if (api == API_device
00272         || api == API_loadimage) /* api dependencies - FIXME - find better way to code these *s */
00273 
00274         apidep = API_render;    
00275     else
00276         apidep = api;
00277 
00278     strncpy(reqtyp, str, TYPBUFSIZ-1);
00279     reqdep = strchr(reqtyp, ':');
00280     if (reqdep) {
00281         *reqdep++ = '\0';
00282         reqpkg = strchr(reqdep, ':');
00283         if (reqpkg)
00284             *reqpkg++ = '\0';
00285     }
00286     else
00287         reqpkg = NULL;
00288 
00289     /* iterate the linked list of plugins for this api */
00290     for (pnext = &(gvc->apis[api]); *pnext; pnext = &((*pnext)->next)) {
00291         strncpy(typ, (*pnext)->typestr, TYPBUFSIZ-1);
00292         dep = strchr(typ, ':');
00293         if (dep) 
00294             *dep++ = '\0';
00295         if (strcmp(typ, reqtyp)) 
00296             continue;  /* types empty or mismatched */
00297         if (dep && reqdep && strcmp(dep, reqdep))
00298             continue;  /* dependencies not empty, but mismatched */
00299         if (! reqpkg || strcmp(reqpkg, (*pnext)->package->name) == 0)
00300         {
00301                 /* found with no packagename constraints, or with required matching packagname */
00302     
00303                 if (dep && (apidep != api)) /* load dependency if needed, continue if can't find */
00304                         if (! (gvplugin_load(gvc, apidep, dep)))
00305                                 continue;
00306                 break;
00307     }
00308     }
00309     rv = *pnext;
00310 
00311     if (rv && rv->typeptr == NULL) {
00312         library = gvplugin_library_load(gvc, rv->package->path);
00313         if (library) {
00314 
00315             /* Now activate the library with real type ptrs */
00316             for (apis = library->apis; (types = apis->types); apis++) {
00317                 for (i = 0; types[i].type; i++) {
00318                     /* NB. quality is not checked or replaced
00319                      *   in case user has manually edited quality in config */
00320                     gvplugin_activate(gvc,
00321                                 apis->api,
00322                                 types[i].type,
00323                                 library->packagename,
00324                                 rv->package->path,
00325                                 &types[i]);
00326                 }
00327             }
00328             if (gvc->common.verbose >= 1)
00329                 fprintf(stderr, "Activated plugin library: %s\n",
00330                         rv->package->path ? rv->package->path : "<builtin>");
00331         }
00332     }
00333 
00334     /* one last check for successfull load */
00335     if (rv && rv->typeptr == NULL)
00336         rv = NULL;
00337 
00338     if (rv && gvc->common.verbose >= 1)
00339         fprintf(stderr, "Using %s: %s:%s\n",
00340                 api_names[api],
00341                 rv->typestr,
00342                 rv->package->name
00343                 );
00344 
00345     gvc->api[api] = rv;
00346     return rv;
00347 }
00348 
00349 /* assemble a string list of available plugins 
00350  * non-re-entrant as character store is shared
00351  */
00352 char *gvplugin_list(GVC_t * gvc, api_t api, const char *str)
00353 {
00354     static int first = 1;
00355     gvplugin_available_t **pnext, **plugin;
00356     char *bp;
00357     char *s, *p, *q, *typestr_last;
00358     boolean new = TRUE;
00359     static agxbuf xb;
00360 
00361     /* check for valid apis[] index */
00362     if (api < 0)
00363         return NULL;
00364 
00365     /* check for valid str */
00366     if (! str)
00367         return NULL;
00368 
00369     if (first) {
00370         agxbinit(&xb, 0, 0);
00371         first = 0;
00372     }
00373 
00374     /* does str have a :path modifier? */
00375 #ifdef WIN32
00376     s = _strdup(str);
00377 #else
00378     s = strdup(str);
00379 #endif
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 }