Graphviz 13.0.0~dev.20250121.0651
Loading...
Searching...
No Matches
gvplugin.c
Go to the documentation of this file.
1/*************************************************************************
2 * Copyright (c) 2011 AT&T Intellectual Property
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License v1.0
5 * which accompanies this distribution, and is available at
6 * https://www.eclipse.org/legal/epl-v10.html
7 *
8 * Contributors: Details at https://graphviz.org
9 *************************************************************************/
10
11#include "config.h"
12
13#include <stdbool.h>
14#include <stddef.h>
15#include <stdio.h>
16#include <string.h>
17#include <unistd.h>
18
19#ifdef ENABLE_LTDL
20#include <ltdl.h>
21#endif
22
23#include <common/types.h>
24#include <gvc/gvc.h>
25#include <gvc/gvplugin.h>
26#include <gvc/gvcjob.h>
27#include <gvc/gvcint.h>
28#include <gvc/gvcproc.h>
29#include <gvc/gvio.h>
30
31#include <common/const.h>
32#include <util/agxbuf.h>
33#include <util/alloc.h>
34#include <util/list.h>
35#include <util/startswith.h>
36#include <util/strcasecmp.h>
37#include <util/strview.h>
38
39/*
40 * Define an apis array of name strings using an enumerated api_t as index.
41 * The enumerated type is defined gvplugin.h. The apis array is
42 * initialized here by redefining ELEM and reinvoking APIS.
43 */
44#define ELEM(x) #x,
45static char *api_names[] = { APIS }; /* "render", "layout", ... */
46
47#undef ELEM
48
49/* translate a string api name to its type, or -1 on error */
51{
52 for (size_t api = 0; api < ARRAY_SIZE(api_names); api++) {
53 if (strcmp(str, api_names[api]) == 0)
54 return (api_t) api;
55 }
56 return -1; /* invalid api */
57}
58
59/* translate api_t into string name, or NULL */
61{
62 if (api >= ARRAY_SIZE(api_names))
63 return NULL;
64 return api_names[api];
65}
66
67/* install a plugin description into the list of available plugins
68 * list is alpha sorted by type (not including :dependency), then
69 * quality sorted within the type, then, if qualities are the same,
70 * last install wins.
71 */
72bool gvplugin_install(GVC_t *gvc, api_t api, const char *typestr, int quality,
73 gvplugin_package_t *package,
74 gvplugin_installed_t *typeptr)
75{
76 gvplugin_available_t *plugin, **pnext;
77 char *t;
78
79 /* duplicate typestr to later save in the plugin list */
80 t = strdup(typestr);
81 if (t == NULL)
82 return false;
83
84 // find the current plugin
85 const strview_t type = strview(typestr, ':');
86
87 /* point to the beginning of the linked list of plugins for this api */
88 pnext = &gvc->apis[api];
89
90 /* keep alpha-sorted and insert new duplicates ahead of old */
91 while (*pnext) {
92
93 // find the next plugin
94 const strview_t next_type = strview((*pnext)->typestr, ':');
95
96 if (strview_cmp(type, next_type) <= 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
104 // find the next plugin
105 const strview_t next_type = strview((*pnext)->typestr, ':');
106
107 if (!strview_eq(type, next_type))
108 break;
109 if (quality >= (*pnext)->quality)
110 break;
111 pnext = &(*pnext)->next;
112 }
113
114 plugin = gv_alloc(sizeof(gvplugin_available_t));
115 plugin->next = *pnext;
116 *pnext = plugin;
117 plugin->typestr = t;
118 plugin->quality = quality;
119 plugin->package = package;
120 plugin->typeptr = typeptr; /* null if not loaded */
121
122 return true;
123}
124
125/* Activate a plugin description in the list of available plugins.
126 * This is used when a plugin-library loaded because of demand for
127 * one of its plugins. It updates the available plugin data with
128 * pointers into the loaded library.
129 * NB the quality value is not replaced as it might have been
130 * manually changed in the config file.
131 */
132static void gvplugin_activate(GVC_t * gvc, api_t api, const char *typestr,
133 const char *name, const char *plugin_path,
134 gvplugin_installed_t * typeptr)
135{
137
138 /* point to the beginning of the linked list of plugins for this api */
139 pnext = gvc->apis[api];
140
141 while (pnext) {
142 if (strcasecmp(typestr, pnext->typestr) == 0
143 && strcasecmp(name, pnext->package->name) == 0
144 && pnext->package->path != 0
145 && strcasecmp(plugin_path, pnext->package->path) == 0) {
146 pnext->typeptr = typeptr;
147 return;
148 }
149 pnext = pnext->next;
150 }
151}
152
154#ifdef ENABLE_LTDL
155 lt_dlhandle hndl;
156 lt_ptr ptr;
157 char *s;
158 size_t len;
159 char *libdir;
160 char *suffix = "_LTX_library";
161
163 return NULL;
164
165 libdir = gvconfig_libdir(gvc);
166 agxbuf fullpath = {0};
167#ifdef _WIN32
168 if (pathname[1] == ':') {
169#else
170 if (pathname[0] == '/') {
171#endif
172 agxbput(&fullpath, pathname);
173 } else {
174 agxbprint(&fullpath, "%s%s%s", libdir, DIRSEP, pathname);
175 }
176
177 if (lt_dlinit()) {
178 agerrorf("failed to init libltdl\n");
179 agxbfree(&fullpath);
180 return NULL;
181 }
182 char *p = agxbuse(&fullpath);
183 hndl = lt_dlopen(p);
184 if (!hndl) {
185 if (access(p, R_OK) == 0) {
186 agwarningf("Could not load \"%s\" - %s\n", p, "It was found, so perhaps one of its dependents was not. Try ldd.");
187 }
188 else {
189 agwarningf("Could not load \"%s\" - %s\n", p, lt_dlerror());
190 }
191 agxbfree(&fullpath);
192 return NULL;
193 }
194 if (gvc->common.verbose >= 2)
195 fprintf(stderr, "Loading %s\n", p);
196
197 s = strrchr(p, DIRSEP[0]);
198 len = strlen(s);
199#if defined(_WIN32) && !defined(__MINGW32__) && !defined(__CYGWIN__)
200 if (len < strlen("/gvplugin_x")) {
201#else
202 if (len < strlen("/libgvplugin_x")) {
203#endif
204 agerrorf("invalid plugin path \"%s\"\n", p);
205 agxbfree(&fullpath);
206 return NULL;
207 }
208 char *sym = gv_alloc(len + strlen(suffix) + 1);
209#if defined(_WIN32) && !defined(__MINGW32__) && !defined(__CYGWIN__)
210 strcpy(sym, s + 1); /* strip leading "/" */
211#else
212 strcpy(sym, s + 4); /* strip leading "/lib" or "/cyg" */
213#endif
214#if defined(__CYGWIN__) || defined(__MINGW32__)
215 s = strchr(sym, '-'); /* strip trailing "-1.dll" */
216#else
217 s = strchr(sym, '.'); /* strip trailing ".so.0" or ".dll" or ".sl" */
218#endif
219 strcpy(s, suffix); /* append "_LTX_library" */
220
221 ptr = lt_dlsym(hndl, sym);
222 if (!ptr) {
223 agerrorf("failed to resolve %s in %s\n", sym, p);
224 free(sym);
225 agxbfree(&fullpath);
226 return NULL;
227 }
228 free(sym);
229 agxbfree(&fullpath);
230 return (gvplugin_library_t *)ptr;
231#else
232 (void)gvc;
233 (void)pathname;
234
235 agerrorf("dynamic loading not available\n");
236 return NULL;
237#endif
238}
239
240
241/* load a plugin of type=str
242 the str can optionally contain one or more ":dependencies"
243
244 examples:
245 png
246 png:cairo
247 fully qualified:
248 png:cairo:cairo
249 png:cairo:gd
250 png:gd:gd
251
252*/
254 FILE *debug) {
255 gvplugin_available_t *pnext, *rv;
256 gvplugin_library_t *library;
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 const strview_t reqtyp = strview(str, ':');
269
270 strview_t reqdep = {0};
271
272 strview_t reqpkg = {0};
273
274 if (reqtyp.data[reqtyp.size] == ':') {
275 reqdep = strview(reqtyp.data + reqtyp.size + strlen(":"), ':');
276 if (reqdep.data[reqdep.size] == ':') {
277 reqpkg = strview(reqdep.data + reqdep.size + strlen(":"), '\0');
278 }
279 }
280
281 agxbuf diag = {0}; // diagnostic messages
282
283 /* iterate the linked list of plugins for this api */
284 for (pnext = gvc->apis[api]; pnext; pnext = pnext->next) {
285 const strview_t typ = strview(pnext->typestr, ':');
286
287 strview_t dep = {0};
288 if (typ.data[typ.size] == ':') {
289 dep = strview(typ.data + typ.size + strlen(":"), '\0');
290 }
291
292 if (!strview_eq(typ, reqtyp)) {
293 agxbprint(&diag, "# type \"%.*s\" did not match \"%.*s\"\n",
294 (int)typ.size, typ.data, (int)reqtyp.size, reqtyp.data);
295 continue; /* types empty or mismatched */
296 }
297 if (dep.data && reqdep.data) {
298 if (!strview_eq(dep, reqdep)) {
299 agxbprint(&diag,
300 "# dependencies \"%.*s\" did not match \"%.*s\"\n",
301 (int)dep.size, dep.data, (int)reqdep.size,
302 reqdep.data);
303 continue; /* dependencies not empty, but mismatched */
304 }
305 }
306 if (!reqpkg.data || strview_str_eq(reqpkg, pnext->package->name)) {
307 // found with no packagename constraints, or with required matching packagename
308
309 if (dep.data && apidep != api) // load dependency if needed, continue if can't find
310 if (!gvplugin_load(gvc, apidep, dep.data, debug)) {
311 agxbprint(&diag,
312 "# plugin loading of dependency \"%.*s\" failed\n",
313 (int)dep.size, dep.data);
314 continue;
315 }
316 break;
317 }
318 }
319 rv = pnext;
320
321 if (rv && rv->typeptr == NULL) {
322 library = gvplugin_library_load(gvc, rv->package->path);
323 if (library) {
324
325 /* Now activate the library with real type ptrs */
326 for (apis = library->apis; (types = apis->types); apis++) {
327 for (i = 0; types[i].type; i++) {
328 /* NB. quality is not checked or replaced
329 * in case user has manually edited quality in config */
330 gvplugin_activate(gvc, apis->api, types[i].type, library->packagename, rv->package->path, &types[i]);
331 }
332 }
333 if (gvc->common.verbose >= 1)
334 fprintf(stderr, "Activated plugin library: %s\n", rv->package->path ? rv->package->path : "<builtin>");
335 }
336 }
337
338 /* one last check for successful load */
339 if (rv && rv->typeptr == NULL) {
340 agxbprint(&diag, "# unsuccessful plugin load\n");
341 rv = NULL;
342 }
343
344 if (rv && gvc->common.verbose >= 1)
345 fprintf(stderr, "Using %s: %s:%s\n", api_names[api], rv->typestr, rv->package->name);
346
347 if (debug != NULL) {
348 fputs(agxbuse(&diag), debug);
349 }
350 agxbfree(&diag);
351
352 gvc->api[api] = rv;
353 return rv;
354}
355
356/* assemble a string list of available plugins
357 * non-re-entrant as character store is shared
358 */
359char *gvplugin_list(GVC_t * gvc, api_t api, const char *str)
360{
361 const gvplugin_available_t *pnext, *plugin;
362 char *bp;
363 bool new = true;
364 static agxbuf xb;
365
366 /* check for valid str */
367 if (!str)
368 return NULL;
369
370 /* does str have a :path modifier? */
371 const strview_t strv = strview(str, ':');
372
373 /* point to the beginning of the linked list of plugins for this api */
374 plugin = gvc->apis[api];
375
376 if (strv.data[strv.size] == ':') { /* if str contains a ':', and if we find a match for the type,
377 then just list the alternative paths for the plugin */
378 for (pnext = plugin; pnext; pnext = pnext->next) {
379 const strview_t type = strview(pnext->typestr, ':');
380 // skip duplicates
381 bool already_seen = false;
382 for (const gvplugin_available_t *p = plugin; p != pnext;
383 p = p->next) {
384 already_seen |= strcasecmp(pnext->typestr, p->typestr) == 0 &&
385 strcasecmp(pnext->package->name, p->package->name) == 0;
386 }
387 if (already_seen) {
388 continue;
389 }
390 // list only the matching type, or all types if str is an empty
391 // string or starts with ":"
392 if (strv.size == 0 || strview_case_eq(strv, type)) {
393 /* list each member of the matching type as "type:path" */
394 agxbprint(&xb, " %s:%s", pnext->typestr, pnext->package->name);
395 new = false;
396 }
397 }
398 }
399 if (new) { /* if the type was not found, or if str without ':',
400 then just list available types */
401 strview_t type_last = {0};
402 for (pnext = plugin; pnext; pnext = pnext->next) {
403 /* list only one instance of type */
404 const strview_t type = strview(pnext->typestr, ':');
405 if (!type_last.data || !strview_case_eq(type_last, type)) {
406 /* list it as "type" i.e. w/o ":path" */
407 agxbprint(&xb, " %.*s", (int)type.size, type.data);
408 new = false;
409 }
410 type_last = type;
411 }
412 }
413 if (new)
414 bp = "";
415 else
416 bp = agxbuse(&xb);
417 return bp;
418}
419
420DEFINE_LIST(strs, char*)
421
422/* gvPluginList:
423 * Return list of plugins of type kind.
424 * The size of the list is stored in sz.
425 * The caller is responsible for freeing the storage. This involves
426 * freeing each item, then the list.
427 * Returns NULL on error, or if there are no plugins.
428 * In the former case, sz is unchanged; in the latter, sz = 0.
429 */
430char **gvPluginList(GVC_t *gvc, const char *kind, int *sz) {
431 size_t api;
432 const gvplugin_available_t *pnext, *plugin;
433 strs_t list = {0};
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 agerrorf("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 strview_t typestr_last = {0};
449 for (pnext = plugin; pnext; pnext = pnext->next) {
450 /* list only one instance of type */
451 strview_t q = strview(pnext->typestr, ':');
452 if (!typestr_last.data || !strview_case_eq(typestr_last, q)) {
453 strs_append(&list, strview_str(q));
454 }
455 typestr_last = q;
456 }
457
458 *sz = (int)strs_size(&list);
459 return strs_detach(&list);
460}
461
463{
464 int api;
465
466#ifdef ENABLE_LTDL
468 fprintf(stderr, "The plugin configuration file:\n\t%s\n", gvc->config_path);
469 if (gvc->config_found)
470 fprintf(stderr, "\t\twas successfully loaded.\n");
471 else
472 fprintf(stderr, "\t\twas not found or not usable. No on-demand plugins.\n");
473 } else {
474 fprintf(stderr, "Demand loading of plugins is disabled.\n");
475 }
476#endif
477
478 for (api = 0; api < (int)ARRAY_SIZE(api_names); api++) {
479 if (gvc->common.verbose >= 2)
480 fprintf(stderr, " %s\t: %s\n", api_names[api], gvplugin_list(gvc, api, ":"));
481 else
482 fprintf(stderr, " %s\t: %s\n", api_names[api], gvplugin_list(gvc, api, "?"));
483 }
484
485}
486
488{
489 Agraph_t *g, *sg, *ssg;
490 Agnode_t *n, *m, *loadimage_n, *renderer_n, *device_n, *textlayout_n, *layout_n;
491 Agedge_t *e;
492 Agsym_t *a;
493 gvplugin_package_t *package;
494 const gvplugin_available_t *pnext;
495 char *p, *q, *lq, *t;
496 int neededge_loadimage, neededge_device;
497
498 g = agopen("G", Agdirected, NULL);
499 agattr(g, AGRAPH, "label", "");
500 agattr(g, AGRAPH, "rankdir", "");
501 agattr(g, AGRAPH, "rank", "");
502 agattr(g, AGRAPH, "ranksep", "");
503 agattr(g, AGNODE, "label", NODENAME_ESC);
504 agattr(g, AGNODE, "shape", "");
505 agattr(g, AGNODE, "style", "");
506 agattr(g, AGNODE, "width", "");
507 agattr(g, AGEDGE, "style", "");
508
509 a = agfindgraphattr(g, "rankdir");
510 agxset(g, a, "LR");
511
512 a = agfindgraphattr(g, "ranksep");
513 agxset(g, a, "2.5");
514
515 a = agfindgraphattr(g, "label");
516 agxset(g, a, "Plugins");
517
518 agxbuf buf = {0};
519 for (package = gvc->packages; package; package = package->next) {
520 loadimage_n = renderer_n = device_n = textlayout_n = layout_n = NULL;
521 neededge_loadimage = neededge_device = 0;
522 agxbprint(&buf, "cluster_%s", package->name);
523 sg = agsubg(g, agxbuse(&buf), 1);
524 a = agfindgraphattr(sg, "label");
525 agxset(sg, a, package->name);
526 for (size_t api = 0; api < ARRAY_SIZE(api_names); api++) {
527 agxbprint(&buf, "%s_%s", package->name, api_names[api]);
528 ssg = agsubg(sg, agxbuse(&buf), 1);
529 a = agfindgraphattr(ssg, "rank");
530 agxset(ssg, a, "same");
531 for (pnext = gvc->apis[api]; pnext; pnext = pnext->next) {
532 if (pnext->package == package) {
533 t = q = gv_strdup(pnext->typestr);
534 if ((p = strchr(q, ':')))
535 *p++ = '\0';
536 /* Now p = renderer, e.g. "gd"
537 * and q = device, e.g. "png"
538 * or q = loadimage, e.g. "png" */
539 switch (api) {
540 case API_device:
541 case API_loadimage:
542 /* draw device as box - record last device in plugin (if any) in device_n */
543 /* draw loadimage as box - record last loadimage in plugin (if any) in loadimage_n */
544
545 /* hack for aliases */
546 lq = q;
547 if (startswith(q, "jp")) {
548 q = "jpg"; /* canonical - for node name */
549 lq = "jpeg\\njpe\\njpg"; /* list - for label */
550 }
551 else if (startswith(q, "tif")) {
552 q = "tif";
553 lq = "tiff\\ntif";
554 }
555 else if (!strcmp(q, "x11") || !strcmp(q, "xlib")) {
556 q = "x11";
557 lq = "x11\\nxlib";
558 }
559 else if (!strcmp(q, "dot") || !strcmp(q, "gv")) {
560 q = "gv";
561 lq = "gv\\ndot";
562 }
563
564 agxbprint(&buf, "%s_%s_%s", package->name,
565 api_names[api], q);
566 n = agnode(ssg, agxbuse(&buf), 1);
567 a = agfindnodeattr(g, "label");
568 agxset(n, a, lq);
569 a = agfindnodeattr(g, "width");
570 agxset(n, a, "1.0");
571 a = agfindnodeattr(g, "shape");
572 if (api == API_device) {
573 agxset(n, a, "box");
574 device_n = n;
575 }
576 else {
577 agxset(n, a, "box");
578 loadimage_n = n;
579 }
580 if (!(p && *p)) {
581 m = agfindnode(sg, "render_cg");
582 if (!m) {
583 m = agnode(sg, "render_cg", 1);
584 a = agfindgraphattr(g, "label");
585 agxset(m, a, "cg");
586 }
587 agedge(sg, m, n, NULL, 1);
588 }
589 break;
590 case API_render:
591 /* draw renderers as ellipses - record last renderer in plugin (if any) in renderer_n */
592 agxbprint(&buf, "%s_%s", api_names[api], q);
593 renderer_n = n = agnode(ssg, agxbuse(&buf), 1);
594 a = agfindnodeattr(g, "label");
595 agxset(n, a, q);
596 break;
597 case API_textlayout:
598 /* draw textlayout as invtriangle - record last textlayout in plugin (if any) in textlayout_n */
599 /* FIXME? only one textlayout is loaded. Why? */
600 agxbprint(&buf, "%s_%s", api_names[api], q);
601 textlayout_n = n = agnode(ssg, agxbuse(&buf), 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 agxbprint(&buf, "%s_%s", api_names[api], q);
610 layout_n = n = agnode(ssg, agxbuse(&buf), 1);
611 a = agfindnodeattr(g, "shape");
612 agxset(n, a, "hexagon");
613 a = agfindnodeattr(g, "label");
614 agxset(n, a, q);
615 break;
616 default:
617 break;
618 }
619 free(t);
620 }
621 }
622 // add some invisible nodes (if needed) and invisible edges to
623 // improve layout of cluster
624 if (api == API_loadimage && !loadimage_n) {
625 neededge_loadimage = 1;
626 agxbprint(&buf, "%s_%s_invis", package->name, api_names[api]);
627 loadimage_n = n = agnode(ssg, agxbuse(&buf), 1);
628 a = agfindnodeattr(g, "style");
629 agxset(n, a, "invis");
630 a = agfindnodeattr(g, "label");
631 agxset(n, a, "");
632 a = agfindnodeattr(g, "width");
633 agxset(n, a, "1.0");
634
635 agxbprint(&buf, "%s_%s_invis_src", package->name,
636 api_names[api]);
637 n = agnode(g, agxbuse(&buf), 1);
638 a = agfindnodeattr(g, "style");
639 agxset(n, a, "invis");
640 a = agfindnodeattr(g, "label");
641 agxset(n, a, "");
642
643 e = agedge(g, n, loadimage_n, NULL, 1);
644 a = agfindedgeattr(g, "style");
645 agxset(e, a, "invis");
646 }
647 if (api == API_render && !renderer_n) {
648 neededge_loadimage = 1;
649 neededge_device = 1;
650 agxbprint(&buf, "%s_%s_invis", package->name, api_names[api]);
651 renderer_n = n = agnode(ssg, agxbuse(&buf), 1);
652 a = agfindnodeattr(g, "style");
653 agxset(n, a, "invis");
654 a = agfindnodeattr(g, "label");
655 agxset(n, a, "");
656 }
657 if (api == API_device && !device_n) {
658 neededge_device = 1;
659 agxbprint(&buf, "%s_%s_invis", package->name, api_names[api]);
660 device_n = n = agnode(ssg, agxbuse(&buf), 1);
661 a = agfindnodeattr(g, "style");
662 agxset(n, a, "invis");
663 a = agfindnodeattr(g, "label");
664 agxset(n, a, "");
665 a = agfindnodeattr(g, "width");
666 agxset(n, a, "1.0");
667 }
668 }
669 if (neededge_loadimage) {
670 e = agedge(sg, loadimage_n, renderer_n, NULL, 1);
671 a = agfindedgeattr(g, "style");
672 agxset(e, a, "invis");
673 }
674 if (neededge_device) {
675 e = agedge(sg, renderer_n, device_n, NULL, 1);
676 a = agfindedgeattr(g, "style");
677 agxset(e, a, "invis");
678 }
679 if (textlayout_n) {
680 e = agedge(sg, loadimage_n, textlayout_n, NULL, 1);
681 a = agfindedgeattr(g, "style");
682 agxset(e, a, "invis");
683 }
684 if (layout_n) {
685 e = agedge(sg, loadimage_n, layout_n, NULL, 1);
686 a = agfindedgeattr(g, "style");
687 agxset(e, a, "invis");
688 }
689 }
690
691 ssg = agsubg(g, "output_formats", 1);
692 a = agfindgraphattr(ssg, "rank");
693 agxset(ssg, a, "same");
694 for (package = gvc->packages; package; package = package->next) {
695 for (size_t api = 0; api < ARRAY_SIZE(api_names); api++) {
696 for (pnext = gvc->apis[api]; pnext; pnext = pnext->next) {
697 if (pnext->package == package) {
698 t = q = gv_strdup(pnext->typestr);
699 if ((p = strchr(q, ':')))
700 *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 lq = q;
707 if (startswith(q, "jp")) {
708 q = "jpg"; /* canonical - for node name */
709 lq = "jpeg\\njpe\\njpg"; /* list - for label */
710 }
711 else if (startswith(q, "tif")) {
712 q = "tif";
713 lq = "tiff\\ntif";
714 }
715 else if (!strcmp(q, "x11") || !strcmp(q, "xlib")) {
716 q = "x11";
717 lq = "x11\\nxlib";
718 }
719 else if (!strcmp(q, "dot") || !strcmp(q, "gv")) {
720 q = "gv";
721 lq = "gv\\ndot";
722 }
723
724 switch (api) {
725 case API_device: {
726 agxbprint(&buf, "%s_%s_%s", package->name,
727 api_names[api], q);
728 n = agnode(g, agxbuse(&buf), 1);
729 agxbprint(&buf, "output_%s", q);
730 char *const output = agxbuse(&buf);
731 m = agfindnode(ssg, output);
732 if (!m) {
733 m = agnode(ssg, output, 1);
734 a = agfindnodeattr(g, "label");
735 agxset(m, a, lq);
736 a = agfindnodeattr(g, "shape");
737 agxset(m, a, "note");
738 }
739 e = agfindedge(g, n, m);
740 if (!e)
741 e = agedge(g, n, m, NULL, 1);
742 if (p && *p) {
743 agxbprint(&buf, "render_%s", p);
744 char *const render = agxbuse(&buf);
745 m = agfindnode(ssg, render);
746 if (!m)
747 m = agnode(g, render, 1);
748 e = agfindedge(g, m, n);
749 if (!e)
750 e = agedge(g, m, n, NULL, 1);
751 }
752 break;
753 }
754 case API_loadimage: {
755 agxbprint(&buf, "%s_%s_%s", package->name,
756 api_names[api], q);
757 n = agnode(g, agxbuse(&buf), 1);
758 agxbprint(&buf, "input_%s", q);
759 char *const input = agxbuse(&buf);
760 m = agfindnode(g, input);
761 if (!m) {
762 m = agnode(g, input, 1);
763 a = agfindnodeattr(g, "label");
764 agxset(m, a, lq);
765 a = agfindnodeattr(g, "shape");
766 agxset(m, a, "note");
767 }
768 e = agfindedge(g, m, n);
769 if (!e)
770 e = agedge(g, m, n, NULL, 1);
771 agxbprint(&buf, "render_%s", p);
772 char *const render = agxbuse(&buf);
773 m = agfindnode(g, render);
774 if (!m)
775 m = agnode(g, render, 1);
776 e = agfindedge(g, n, m);
777 if (!e)
778 e = agedge(g, n, m, NULL, 1);
779 break;
780 }
781 default:
782 break;
783 }
784 free(t);
785 }
786 }
787 }
788 }
789
790 agxbfree(&buf);
791 return g;
792}
static void agxbfree(agxbuf *xb)
free any malloced resources
Definition agxbuf.h:78
static int agxbprint(agxbuf *xb, const char *fmt,...)
Printf-style output to an agxbuf.
Definition agxbuf.h:234
static WUR char * agxbuse(agxbuf *xb)
Definition agxbuf.h:307
Memory allocation wrappers that exit on failure.
static char * gv_strdup(const char *original)
Definition alloc.h:101
static void * gv_alloc(size_t size)
Definition alloc.h:47
char * suffix
Definition bcomps.c:66
#define NODENAME_ESC
Definition const.h:80
expr procedure type
Definition exparse.y:208
static double len(glCompPoint p)
Definition glutils.c:150
void free(void *)
node NULL
Definition grammar.y:163
Agsym_t * agattr(Agraph_t *g, int kind, char *name, const char *value)
creates or looks up attributes of a graph
Definition attr.c:371
int agxset(void *obj, Agsym_t *sym, const char *value)
Definition attr.c:532
Agedge_t * agedge(Agraph_t *g, Agnode_t *t, Agnode_t *h, char *name, int createflag)
Definition edge.c:256
#define agfindedgeattr(g, a)
Definition types.h:617
#define agfindedge(g, t, h)
Definition types.h:609
void agwarningf(const char *fmt,...)
Definition agerror.c:173
void agerrorf(const char *fmt,...)
Definition agerror.c:165
#define agfindgraphattr(g, a)
Definition types.h:613
Agraph_t * agopen(char *name, Agdesc_t desc, Agdisc_t *disc)
creates a new graph with the given name and kind
Definition graph.c:44
Agdesc_t Agdirected
directed
Definition graph.c:280
Agnode_t * agnode(Agraph_t *g, char *name, int createflag)
Definition node.c:140
#define agfindnodeattr(g, a)
Definition types.h:615
#define agfindnode(g, n)
Definition types.h:611
@ AGEDGE
Definition cgraph.h:207
@ AGNODE
Definition cgraph.h:207
@ AGRAPH
Definition cgraph.h:207
Agraph_t * agsubg(Agraph_t *g, char *name, int cflag)
Definition subg.c:55
char ** gvPluginList(GVC_t *gvc, const char *kind, int *sz)
Definition gvplugin.c:430
static GVC_t * gvc
Definition gv.cpp:23
bool render(Agraph_t *g)
Definition gv.cpp:624
Graphviz context library.
#define APIS
Definition gvcext.h:26
api_t
Definition gvcext.h:32
#define DIRSEP
Definition gvcint.h:163
#define ARRAY_SIZE(A)
Definition gvcjob.h:26
char * gvconfig_libdir(GVC_t *gvc)
agxbput(xb, staging)
void gvplugin_write_status(GVC_t *gvc)
Definition gvplugin.c:462
static char * api_names[]
Definition gvplugin.c:45
gvplugin_library_t * gvplugin_library_load(GVC_t *gvc, const char *pathname)
Definition gvplugin.c:153
static void gvplugin_activate(GVC_t *gvc, api_t api, const char *typestr, const char *name, const char *plugin_path, gvplugin_installed_t *typeptr)
Definition gvplugin.c:132
api_t gvplugin_api(const char *str)
Definition gvplugin.c:50
gvplugin_available_t * gvplugin_load(GVC_t *gvc, api_t api, const char *str, FILE *debug)
Definition gvplugin.c:253
Agraph_t * gvplugin_graph(GVC_t *gvc)
Definition gvplugin.c:487
char * gvplugin_api_name(api_t api)
Definition gvplugin.c:60
bool gvplugin_install(GVC_t *gvc, api_t api, const char *typestr, int quality, gvplugin_package_t *package, gvplugin_installed_t *typeptr)
Definition gvplugin.c:72
char * gvplugin_list(GVC_t *gvc, api_t api, const char *str)
Definition gvplugin.c:359
static gvplugin_api_t apis[]
textitem scanner parser str
Definition htmlparse.y:224
#define DEFINE_LIST(name, type)
Definition list.h:21
static bool startswith(const char *s, const char *prefix)
does the string s begin with the string prefix?
Definition startswith.h:11
platform abstraction for case-insensitive string functions
graph or subgraph
Definition cgraph.h:424
string attribute descriptor symbol in Agattr_s.dict
Definition cgraph.h:641
int demand_loading
Definition gvcommon.h:32
int verbose
Definition gvcommon.h:22
Definition gvcint.h:80
char * config_path
Definition gvcint.h:83
bool config_found
Definition gvcint.h:84
GVCOMMON_t common
Definition gvcint.h:81
gvplugin_package_t * packages
Definition gvcint.h:100
gvplugin_available_t * apis[APIS]
Definition gvcint.h:97
gvplugin_available_t * api[APIS]
Definition gvcint.h:98
size_t size
number of characters in the buffer
Definition agxbuf.h:58
gvplugin_installed_t * types
Definition gvplugin.h:53
gvplugin_package_t * package
Definition gvcint.h:64
gvplugin_available_t * next
Definition gvcint.h:60
gvplugin_installed_t * typeptr
Definition gvcint.h:65
ingroup plugin_api
Definition gvplugin.h:35
const char * type
Definition gvplugin.h:41
gvplugin_api_t * apis
Definition gvplugin.h:59
a non-owning string reference
Definition strview.h:20
const char * data
start of the pointed to string
Definition strview.h:21
size_t size
extent of the string in bytes
Definition strview.h:22
Non-owning string references.
static bool strview_str_eq(strview_t a, const char *b)
compare a string reference to a string for equality
Definition strview.h:98
static int strview_cmp(strview_t a, strview_t b)
compare two string references
Definition strview.h:71
static char * strview_str(strview_t source)
make a heap-allocated string from this string view
Definition strview.h:41
static bool strview_eq(strview_t a, strview_t b)
compare two string references for equality
Definition strview.h:89
static bool strview_case_eq(strview_t a, strview_t b)
compare two string references for case insensitive equality
Definition strview.h:49
static strview_t strview(const char *referent, char terminator)
create a string reference
Definition strview.h:26
graphs, nodes and edges info: Agraphinfo_t, Agnodeinfo_t and Agedgeinfo_t
Definition grammar.c:93