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