Graphviz  2.35.20130930.0449
gvplugin.c
Go to the documentation of this file.
1 /* $Id$ $Revision$ */
2 /* vim:set shiftwidth=4 ts=8: */
3 
4 /*************************************************************************
5  * Copyright (c) 2011 AT&T Intellectual Property
6  * All rights reserved. This program and the accompanying materials
7  * are made available under the terms of the Eclipse Public License v1.0
8  * which accompanies this distribution, and is available at
9  * http://www.eclipse.org/legal/epl-v10.html
10  *
11  * Contributors: See CVS logs. Details at http://www.graphviz.org/
12  *************************************************************************/
13 
14 #ifdef HAVE_CONFIG_H
15 #include "config.h"
16 #endif
17 
18 #include <string.h>
19 #ifdef ENABLE_LTDL
20 #include <ltdl.h>
21 #endif
22 
23 #include <agxbuf.h>
24 #include "memory.h"
25 #include "types.h"
26 #include "gvplugin.h"
27 #include "gvcjob.h"
28 #include "gvcint.h"
29 #include "gvcproc.h"
30 #include "gvio.h"
31 
32 #include "const.h"
33 
34 #ifndef HAVE_STRCASECMP
35 extern int strcasecmp(const char *s1, const char *s2);
36 #endif
37 
38 #ifdef WIN32
39 #define strdup(x) _strdup(x)
40 #endif
41 
42 /*
43  * Define an apis array of name strings using an enumerated api_t as index.
44  * The enumerated type is defined gvplugin.h. The apis array is
45  * inititialized here by redefining ELEM and reinvoking APIS.
46  */
47 #define ELEM(x) #x,
48 static char *api_names[] = { APIS }; /* "render", "layout", ... */
49 #undef ELEM
50 
51 /* translate a string api name to its type, or -1 on error */
53 {
54  int api;
55 
56  for (api = 0; api < ARRAY_SIZE(api_names); api++) {
57  if (strcmp(str, api_names[api]) == 0)
58  return (api_t)api;
59  }
60  return -1; /* invalid api */
61 }
62 
63 /* translate api_t into string name, or NULL */
65 {
66  if (api < 0 || api >= ARRAY_SIZE(api_names))
67  return NULL;
68  return api_names[api];
69 }
70 
71 /* install a plugin description into the list of available plugins
72  * list is alpha sorted by type (not including :dependency), then
73  * quality sorted within the type, then, if qualities are the same,
74  * last install wins.
75  */
76 boolean gvplugin_install(GVC_t * gvc, api_t api, const char *typestr,
77  int quality, gvplugin_package_t *package,
78  gvplugin_installed_t * typeptr)
79 {
80  gvplugin_available_t *plugin, **pnext;
81 #define TYPSIZ 63
82  char *p, pins[TYPSIZ+1], pnxt[TYPSIZ+1];
83 
84  if (api < 0)
85  return FALSE;
86 
87  strncpy(pins, typestr, TYPSIZ);
88  if ((p = strchr(pins, ':')))
89  *p = '\0';
90 
91  /* point to the beginning of the linked list of plugins for this api */
92  pnext = &(gvc->apis[api]);
93 
94  /* keep alpha-sorted and insert new duplicates ahead of old */
95  while (*pnext) {
96  strncpy(pnxt, (*pnext)->typestr, TYPSIZ);
97  if ((p = strchr(pnxt, ':')))
98  *p = '\0';
99  if (strcmp(pins, pnxt) <= 0)
100  break;
101  pnext = &((*pnext)->next);
102  }
103 
104  /* keep quality sorted within type and insert new duplicates ahead of old */
105  while (*pnext) {
106  strncpy(pnxt, (*pnext)->typestr, TYPSIZ);
107  if ((p = strchr(pnxt, ':')))
108  *p = '\0';
109  if (strcmp(pins, pnxt) != 0)
110  break;
111  if (quality >= (*pnext)->quality)
112  break;
113  pnext = &((*pnext)->next);
114  }
115 
116  plugin = GNEW(gvplugin_available_t);
117  plugin->next = *pnext;
118  *pnext = plugin;
119  plugin->typestr = typestr;
120  plugin->quality = quality;
121  plugin->package = package;
122  plugin->typeptr = typeptr; /* null if not loaded */
123 
124  return TRUE;
125 }
126 
127 /* Activate a plugin description in the list of available plugins.
128  * This is used when a plugin-library loaded because of demand for
129  * one of its plugins. It updates the available plugin data with
130  * pointers into the loaded library.
131  * NB the quality value is not replaced as it might have been
132  * manually changed in the config file.
133  */
134 static boolean gvplugin_activate(GVC_t * gvc, api_t api,
135  const char *typestr, char *name, char *path,
136  gvplugin_installed_t * typeptr)
137 {
138  gvplugin_available_t **pnext;
139 
140 
141  if (api < 0)
142  return FALSE;
143 
144  /* point to the beginning of the linked list of plugins for this api */
145  pnext = &(gvc->apis[api]);
146 
147  while (*pnext) {
148  if ( (strcasecmp(typestr, (*pnext)->typestr) == 0)
149  && (strcasecmp(name, (*pnext)->package->name) == 0)
150  && ((*pnext)->package->path != 0)
151  && (strcasecmp(path, (*pnext)->package->path) == 0)) {
152  (*pnext)->typeptr = typeptr;
153  return TRUE;
154  }
155  pnext = &((*pnext)->next);
156  }
157  return FALSE;
158 }
159 
161 {
162 #ifdef ENABLE_LTDL
163  lt_dlhandle hndl;
164  lt_ptr ptr;
165  char *s, *sym;
166  int len;
167  static char *p;
168  static int lenp;
169  char *libdir;
170  char *suffix = "_LTX_library";
171 
172  if (! gvc->common.demand_loading)
173  return NULL;
174 
175  libdir = gvconfig_libdir(gvc);
176  len = strlen(libdir) + 1 + strlen(path) + 1;
177  if (len > lenp) {
178  lenp = len+20;
179  if (p)
180  p = grealloc(p, lenp);
181  else
182  p = gmalloc(lenp);
183  }
184 
185 #ifdef WIN32
186  if (path[1] == ':') {
187 #else
188  if (path[0] == '/') {
189 #endif
190  strcpy(p, path);
191  } else {
192  strcpy(p, libdir);
193  strcat(p, DIRSEP);
194  strcat(p, path);
195  }
196 
197  if (lt_dlinit()) {
198  agerr(AGERR, "failed to init libltdl\n");
199  return NULL;
200  }
201  hndl = lt_dlopen (p);
202  if (!hndl) {
203  agerr(AGWARN, "Could not load \"%s\" - %s\n", p, (char*)lt_dlerror());
204  return NULL;
205  }
206  if (gvc->common.verbose >= 2)
207  fprintf(stderr, "Loading %s\n", p);
208 
209  s = strrchr(p, DIRSEP[0]);
210  len = strlen(s);
211 #if defined(WIN32) && !defined(__MINGW32__) && !defined(__CYGWIN__)
212  if (len < strlen("/gvplugin_x")) {
213 #else
214  if (len < strlen("/libgvplugin_x")) {
215 #endif
216  agerr (AGERR,"invalid plugin path \"%s\"\n", p);
217  return NULL;
218  }
219  sym = gmalloc(len + strlen(suffix) + 1);
220 #if defined(WIN32) && !defined(__MINGW32__) && !defined(__CYGWIN__)
221  strcpy(sym, s+1); /* strip leading "/" */
222 #else
223  strcpy(sym, s+4); /* strip leading "/lib" or "/cyg" */
224 #endif
225 #if defined(__CYGWIN__) || defined(__MINGW32__)
226  s = strchr(sym, '-'); /* strip trailing "-1.dll" */
227 #else
228  s = strchr(sym, '.'); /* strip trailing ".so.0" or ".dll" or ".sl" */
229 #endif
230  strcpy(s,suffix); /* append "_LTX_library" */
231 
232  ptr = lt_dlsym (hndl, sym);
233  if (!ptr) {
234  agerr (AGERR,"failed to resolve %s in %s\n", sym, p);
235  free(sym);
236  return NULL;
237  }
238  free(sym);
239  return (gvplugin_library_t *)(ptr);
240 #else
241  agerr (AGERR,"dynamic loading not available\n");
242  return NULL;
243 #endif
244 }
245 
246 
247 /* load a plugin of type=str
248  the str can optionally contain one or more ":dependencies"
249 
250  examples:
251  png
252  png:cairo
253  fully qualified:
254  png:cairo:cairo
255  png:cairo:gd
256  png:gd:gd
257 
258 */
260 {
261  gvplugin_available_t **pnext, *rv;
262  gvplugin_library_t *library;
263  gvplugin_api_t *apis;
264  gvplugin_installed_t *types;
265 #define TYPBUFSIZ 64
266  char reqtyp[TYPBUFSIZ], typ[TYPBUFSIZ];
267  char *reqdep, *dep = NULL, *reqpkg;
268  int i;
269  api_t apidep;
270 
271  /* check for valid apis[] index */
272  if (api < 0)
273  return NULL;
274 
275  if (api == API_device
276  || api == API_loadimage) /* api dependencies - FIXME - find better way to code these *s */
277 
278  apidep = API_render;
279  else
280  apidep = api;
281 
282  strncpy(reqtyp, str, TYPBUFSIZ-1);
283  reqdep = strchr(reqtyp, ':');
284  if (reqdep) {
285  *reqdep++ = '\0';
286  reqpkg = strchr(reqdep, ':');
287  if (reqpkg)
288  *reqpkg++ = '\0';
289  }
290  else
291  reqpkg = NULL;
292 
293  /* iterate the linked list of plugins for this api */
294  for (pnext = &(gvc->apis[api]); *pnext; pnext = &((*pnext)->next)) {
295  strncpy(typ, (*pnext)->typestr, TYPBUFSIZ-1);
296  dep = strchr(typ, ':');
297  if (dep)
298  *dep++ = '\0';
299  if (strcmp(typ, reqtyp))
300  continue; /* types empty or mismatched */
301  if (dep && reqdep && strcmp(dep, reqdep))
302  continue; /* dependencies not empty, but mismatched */
303  if (! reqpkg || strcmp(reqpkg, (*pnext)->package->name) == 0)
304  {
305  /* found with no packagename constraints, or with required matching packagname */
306 
307  if (dep && (apidep != api)) /* load dependency if needed, continue if can't find */
308  if (! (gvplugin_load(gvc, apidep, dep)))
309  continue;
310  break;
311  }
312  }
313  rv = *pnext;
314 
315  if (rv && rv->typeptr == NULL) {
316  library = gvplugin_library_load(gvc, rv->package->path);
317  if (library) {
318 
319  /* Now activate the library with real type ptrs */
320  for (apis = library->apis; (types = apis->types); apis++) {
321  for (i = 0; types[i].type; i++) {
322  /* NB. quality is not checked or replaced
323  * in case user has manually edited quality in config */
324  gvplugin_activate(gvc,
325  apis->api,
326  types[i].type,
327  library->packagename,
328  rv->package->path,
329  &types[i]);
330  }
331  }
332  if (gvc->common.verbose >= 1)
333  fprintf(stderr, "Activated plugin library: %s\n",
334  rv->package->path ? rv->package->path : "<builtin>");
335  }
336  }
337 
338  /* one last check for successfull load */
339  if (rv && rv->typeptr == NULL)
340  rv = NULL;
341 
342  if (rv && gvc->common.verbose >= 1)
343  fprintf(stderr, "Using %s: %s:%s\n",
344  api_names[api],
345  rv->typestr,
346  rv->package->name
347  );
348 
349  gvc->api[api] = rv;
350  return rv;
351 }
352 
353 /* assemble a string list of available plugins
354  * non-re-entrant as character store is shared
355  */
356 char *gvplugin_list(GVC_t * gvc, api_t api, const char *str)
357 {
358  static int first = 1;
359  gvplugin_available_t **pnext, **plugin;
360  char *bp;
361  char *s, *p, *q, *typestr_last;
362  boolean new = TRUE;
363  static agxbuf xb;
364 
365  /* check for valid apis[] index */
366  if (api < 0)
367  return NULL;
368 
369  /* check for valid str */
370  if (! str)
371  return NULL;
372 
373  if (first) {
374  agxbinit(&xb, 0, 0);
375  first = 0;
376  }
377 
378  /* does str have a :path modifier? */
379  s = strdup(str);
380  p = strchr(s, ':');
381  if (p)
382  *p++ = '\0';
383 
384  /* point to the beginning of the linked list of plugins for this api */
385  plugin = &(gvc->apis[api]);
386 
387  if (p) { /* if str contains a ':', and if we find a match for the type,
388  then just list the alternative paths for the plugin */
389  for (pnext = plugin; *pnext; pnext = &((*pnext)->next)) {
390  q = strdup((*pnext)->typestr);
391  if ((p = strchr(q, ':')))
392  *p++ = '\0';
393  /* list only the matching type, or all types if s is an empty string */
394  if (!s[0] || strcasecmp(s, q) == 0) {
395  /* list each member of the matching type as "type:path" */
396  agxbputc(&xb,' '); agxbput(&xb, (*pnext)->typestr);
397  agxbputc(&xb,':'); agxbput(&xb, (*pnext)->package->name);
398  new = FALSE;
399  }
400  free(q);
401  }
402  }
403  free(s);
404  if (new) { /* if the type was not found, or if str without ':',
405  then just list available types */
406  typestr_last = NULL;
407  for (pnext = plugin; *pnext; pnext = &((*pnext)->next)) {
408  /* list only one instance of type */
409  q = strdup((*pnext)->typestr);
410  if ((p = strchr(q, ':')))
411  *p++ = '\0';
412  if (!typestr_last || strcasecmp(typestr_last, q) != 0) {
413  /* list it as "type" i.e. w/o ":path" */
414  agxbputc(&xb,' '); agxbput(&xb, q);
415  new = FALSE;
416  }
417  if(!typestr_last)
418  free(typestr_last);
419  typestr_last = q;
420  }
421  if(!typestr_last)
422  free(typestr_last);
423  }
424  if (new)
425  bp = "";
426  else
427  bp = agxbuse(&xb);
428  return bp;
429 }
430 
431 /* gvPluginList:
432  * Return list of plugins of type kind.
433  * The size of the list is stored in sz.
434  * The caller is responsible for freeing the storage. This involves
435  * freeing each item, then the list.
436  * Returns NULL on error, or if there are no plugins.
437  * In the former case, sz is unchanged; in the latter, sz = 0.
438  *
439  * At present, the str argument is unused, but may be used to modify
440  * the search as in gvplugin_list above.
441  */
442 char **gvPluginList(GVC_t * gvc, char* kind, int* sz, const char *str)
443 {
444  int api;
445  gvplugin_available_t **pnext, **plugin;
446  int cnt = 0;
447  char** list = NULL;
448  char *p, *q, *typestr_last;
449 
450  if (!kind) return NULL;
451  for (api = 0; api < ARRAY_SIZE(api_names); api++) {
452  if (!strcasecmp(kind,api_names[api]))
453  break;
454  }
455  if (api == ARRAY_SIZE(api_names)) {
456  agerr(AGERR, "unrecognized api name \"%s\"\n", kind);
457  return NULL;
458  }
459 
460  /* point to the beginning of the linked list of plugins for this api */
461  plugin = &(gvc->apis[api]);
462  typestr_last = NULL;
463  for (pnext = plugin; *pnext; pnext = &((*pnext)->next)) {
464  /* list only one instance of type */
465  q = strdup((*pnext)->typestr);
466  if ((p = strchr(q, ':')))
467  *p++ = '\0';
468  if (!typestr_last || strcasecmp(typestr_last, q) != 0) {
469  list = RALLOC(cnt+1,list,char*);
470  list[cnt++] = q;
471  }
472  typestr_last = q;
473  }
474 
475  *sz = cnt;
476  return list;
477 }
478 
480 {
481  int api;
482 
483 #ifdef ENABLE_LTDL
484  if (gvc->common.demand_loading) {
485  fprintf(stderr,"The plugin configuration file:\n\t%s\n", gvc->config_path);
486  if (gvc->config_found)
487  fprintf(stderr,"\t\twas successfully loaded.\n");
488  else
489  fprintf(stderr,"\t\twas not found or not usable. No on-demand plugins.\n");
490  }
491  else {
492  fprintf(stderr,"Demand loading of plugins is disabled.\n");
493  }
494 #endif
495 
496  for (api = 0; api < ARRAY_SIZE(api_names); api++) {
497  if (gvc->common.verbose >= 2)
498  fprintf(stderr," %s\t: %s\n", api_names[api], gvplugin_list(gvc, api, ":"));
499  else
500  fprintf(stderr," %s\t: %s\n", api_names[api], gvplugin_list(gvc, api, "?"));
501  }
502 
503 }
504 
506 {
507  Agraph_t *g, *sg, *ssg;
508  Agnode_t *n, *m;
509  Agedge_t *e;
510  Agsym_t *a;
511  gvplugin_package_t *package;
512  gvplugin_available_t **pnext;
513  char bufa[100], *buf1, *buf2, bufb[100], *p, *q, *t;
514  int api, found;
515 
516 #ifndef WITH_CGRAPH
517  aginit();
519  /* set persistent attributes here */
520  agraphattr(NULL, "label", "");
521  agraphattr(NULL, "rankdir", "");
522  agraphattr(NULL, "rank", "");
523  agraphattr(NULL, "ranksep", "");
524  agnodeattr(NULL, "label", NODENAME_ESC);
525 
526  g = agopen("G", AGDIGRAPH);
527 #else
528  g = agopen("G", Agdirected, NIL(Agdisc_t *));
529  agattr(g, AGRAPH, "label", "");
530  agattr(g, AGRAPH, "rankdir", "");
531  agattr(g, AGRAPH, "rank", "");
532  agattr(g, AGRAPH, "ranksep", "");
533  agattr(g, AGNODE, "label", NODENAME_ESC);
534 #endif
535 
536  a = agfindgraphattr(g, "rankdir");
537 #ifndef WITH_CGRAPH
538  agxset(g, a->index, "LR");
539 #else
540  agxset(g, a, "LR");
541 #endif
542 
543  a = agfindgraphattr(g, "ranksep");
544 #ifndef WITH_CGRAPH
545  agxset(g, a->index, "1.5");
546 #else
547  agxset(g, a, "1.5");
548 #endif
549 
550  a = agfindgraphattr(g, "label");
551 #ifndef WITH_CGRAPH
552  agxset(g, a->index, "Plugins");
553 #else
554  agxset(g, a, "Plugins");
555 #endif
556 
557  for (package = gvc->packages; package; package = package->next) {
558  strcpy(bufa, "cluster_");
559  strcat(bufa, package->name);
560 #ifndef WITH_CGRAPH
561  sg = agsubg(g, bufa);
562 #else
563  sg = agsubg(g, bufa, 1);
564 #endif
565  a = agfindgraphattr(sg, "label");
566 #ifndef WITH_CGRAPH
567  agxset(sg, a->index, package->name);
568 #else
569  agxset(sg, a, package->name);
570 #endif
571  strcpy(bufa, package->name);
572  strcat(bufa, "_");
573  buf1 = bufa + strlen(bufa);
574  for (api = 0; api < ARRAY_SIZE(api_names); api++) {
575  found = 0;
576  strcpy(buf1, api_names[api]);
577 #ifndef WITH_CGRAPH
578  ssg = agsubg(sg, bufa);
579 #else
580  ssg = agsubg(sg, bufa, 1);
581 #endif
582  a = agfindgraphattr(ssg, "rank");
583 #ifndef WITH_CGRAPH
584  agxset(ssg, a->index, "same");
585 #else
586  agxset(ssg, a, "same");
587 #endif
588  strcat(buf1, "_");
589  buf2 = bufa + strlen(bufa);
590  for (pnext = &(gvc->apis[api]); *pnext; pnext = &((*pnext)->next)) {
591  if ((*pnext)->package == package) {
592  found++;
593  t = q = strdup((*pnext)->typestr);
594  if ((p = strchr(q, ':'))) *p++ = '\0';
595  /* Now p = renderer, e.g. "gd"
596  * and q = device, e.g. "png"
597  * or q = imageloader, e.g. "png" */
598  switch (api) {
599  case API_device:
600  case API_loadimage:
601 
602  /* hack for aliases */
603  if (!strncmp(q,"jp",2))
604  q = "jpeg/jpe/jpg";
605  else if (!strncmp(q,"tif",3))
606  q = "tiff/tif";
607  else if (!strcmp(q,"x11") || !strcmp(q,"xlib"))
608  q = "x11/xlib";
609  else if (!strcmp(q,"dot") || !strcmp(q,"gv"))
610  q = "gv/dot";
611 
612  strcpy(buf2, q);
613 #ifndef WITH_CGRAPH
614  n = agnode(ssg, bufa);
615 #else
616  n = agnode(ssg, bufa, 1);
617 #endif
618  a = agfindnodeattr(g, "label");
619 #ifndef WITH_CGRAPH
620  agxset(n, a->index, q);
621 #else
622  agxset(n, a, q);
623 #endif
624  if (! (p && *p)) {
625  strcpy(bufb, "render_cg");
626  m = agfindnode(sg, bufb);
627  if (!m) {
628 #ifndef WITH_CGRAPH
629  m = agnode(sg, bufb);
630 #else
631  m = agnode(sg, bufb, 1);
632 #endif
633  a = agfindgraphattr(g, "label");
634 #ifndef WITH_CGRAPH
635  agxset(m, a->index, "cg");
636 #else
637  agxset(m, a, "cg");
638 #endif
639  }
640 #ifndef WITH_CGRAPH
641  agedge(sg, m, n);
642 #else
643  agedge(sg, m, n, NULL, 1);
644 #endif
645  }
646  break;
647  case API_render:
648  strcpy(bufb, api_names[api]);
649  strcat(bufb, "_");
650  strcat(bufb, q);
651 #ifndef WITH_CGRAPH
652  n = agnode(ssg, bufb);
653 #else
654  n = agnode(ssg, bufb, 1);
655 #endif
656  a = agfindnodeattr(g, "label");
657 #ifndef WITH_CGRAPH
658  agxset(n, a->index, q);
659 #else
660  agxset(n, a, q);
661 #endif
662  break;
663  default:
664  break;
665  }
666  free(t);
667  }
668  }
669  if (!found)
670 #ifndef WITH_CGRAPH
671  agdelete(ssg->meta_node->graph, ssg->meta_node);
672 #else
673  agdelete(sg, ssg);
674 #endif
675  }
676  }
677 
678 #ifndef WITH_CGRAPH
679  ssg = agsubg(g, "output_formats");
680 #else
681  ssg = agsubg(g, "output_formats", 1);
682 #endif
683  a = agfindgraphattr(ssg, "rank");
684 #ifndef WITH_CGRAPH
685  agxset(ssg, a->index, "same");
686 #else
687  agxset(ssg, a, "same");
688 #endif
689  for (package = gvc->packages; package; package = package->next) {
690  strcpy(bufa, package->name);
691  strcat(bufa, "_");
692  buf1 = bufa + strlen(bufa);
693  for (api = 0; api < ARRAY_SIZE(api_names); api++) {
694  strcpy(buf1, api_names[api]);
695  strcat(buf1, "_");
696  buf2 = bufa + strlen(bufa);
697  for (pnext = &(gvc->apis[api]); *pnext; pnext = &((*pnext)->next)) {
698  if ((*pnext)->package == package) {
699  t = q = strdup((*pnext)->typestr);
700  if ((p = strchr(q, ':'))) *p++ = '\0';
701  /* Now p = renderer, e.g. "gd"
702  * and q = device, e.g. "png"
703  * or q = imageloader, e.g. "png" */
704 
705  /* hack for aliases */
706  if (!strncmp(q,"jp",2))
707  q = "jpeg/jpe/jpg";
708  else if (!strncmp(q,"tif",3))
709  q = "tiff/tif";
710  else if (!strcmp(q,"x11") || !strcmp(q,"xlib"))
711  q = "x11/xlib";
712  else if (!strcmp(q,"dot") || !strcmp(q,"gv"))
713  q = "gv/dot";
714 
715  switch (api) {
716  case API_device:
717  strcpy(buf2, q);
718 #ifndef WITH_CGRAPH
719  n = agnode(g, bufa);
720 #else
721  n = agnode(g, bufa, 1);
722 #endif
723  strcpy(bufb, "output_");
724  strcat(bufb, q);
725  m = agfindnode(ssg, bufb);
726  if (!m) {
727 #ifndef WITH_CGRAPH
728  m = agnode(ssg, bufb);
729 #else
730  m = agnode(ssg, bufb, 1);
731 #endif
732  a = agfindnodeattr(g, "label");
733 #ifndef WITH_CGRAPH
734  agxset(m, a->index, q);
735 #else
736  agxset(m, a, q);
737 #endif
738  }
739  e = agfindedge(g, n, m);
740  if (!e)
741 #ifndef WITH_CGRAPH
742  e = agedge(g, n, m);
743 #else
744  e = agedge(g, n, m, NULL, 1);
745 #endif
746  if (p && *p) {
747  strcpy(bufb, "render_");
748  strcat(bufb, p);
749  m = agfindnode(ssg, bufb);
750  if (!m)
751 #ifndef WITH_CGRAPH
752  m = agnode(g, bufb);
753 #else
754  m = agnode(g, bufb, 1);
755 #endif
756  e = agfindedge(g, m, n);
757  if (!e)
758 #ifndef WITH_CGRAPH
759  e = agedge(g, m, n);
760 #else
761  e = agedge(g, m, n, NULL, 1);
762 #endif
763  }
764  break;
765  case API_loadimage:
766  strcpy(buf2, q);
767 #ifndef WITH_CGRAPH
768  n = agnode(g, bufa);
769 #else
770  n = agnode(g, bufa, 1);
771 #endif
772  strcpy(bufb, "input_");
773  strcat(bufb, q);
774  m = agfindnode(g, bufb);
775  if (!m) {
776 #ifndef WITH_CGRAPH
777  m = agnode(g, bufb);
778 #else
779  m = agnode(g, bufb, 1);
780 #endif
781  a = agfindnodeattr(g, "label");
782 #ifndef WITH_CGRAPH
783  agxset(m, a->index, q);
784 #else
785  agxset(m, a, q);
786 #endif
787  }
788  e = agfindedge(g, m, n);
789  if (!e)
790 #ifndef WITH_CGRAPH
791  e = agedge(g, m, n);
792 #else
793  e = agedge(g, m, n, NULL, 1);
794 #endif
795  strcpy(bufb, "render_");
796  strcat(bufb, p);
797  m = agfindnode(g, bufb);
798  if (!m)
799 #ifndef WITH_CGRAPH
800  m = agnode(g, bufb);
801 #else
802  m = agnode(g, bufb, 1);
803 #endif
804  e = agfindedge(g, n, m);
805  if (!e)
806 #ifndef WITH_CGRAPH
807  e = agedge(g, n, m);
808 #else
809  e = agedge(g, n, m, NULL, 1);
810 #endif
811  break;
812  default:
813  break;
814  }
815  free(t);
816  }
817  }
818  }
819  }
820 
821  return g;
822 }