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