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