Graphviz 13.1.3~dev.20250831.0023
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
421/* gvPluginList:
422 * Return list of plugins of type kind.
423 * The size of the list is stored in sz.
424 * The caller is responsible for freeing the storage. This involves
425 * freeing each item, then the list.
426 * Returns NULL on error, or if there are no plugins.
427 * In the former case, sz is unchanged; in the latter, sz = 0.
428 */
429char **gvPluginList(GVC_t *gvc, const char *kind, int *sz) {
430 size_t api;
431 const gvplugin_available_t *pnext, *plugin;
432 LIST(char *) list = {0};
433
434 if (!kind)
435 return NULL;
436 for (api = 0; api < ARRAY_SIZE(api_names); api++) {
437 if (!strcasecmp(kind, api_names[api]))
438 break;
439 }
440 if (api == ARRAY_SIZE(api_names)) {
441 agerrorf("unrecognized api name \"%s\"\n", kind);
442 return NULL;
443 }
444
445 /* point to the beginning of the linked list of plugins for this api */
446 plugin = gvc->apis[api];
447 strview_t typestr_last = {0};
448 for (pnext = plugin; pnext; pnext = pnext->next) {
449 /* list only one instance of type */
450 strview_t q = strview(pnext->typestr, ':');
451 if (!typestr_last.data || !strview_case_eq(typestr_last, q)) {
452 LIST_APPEND(&list, strview_str(q));
453 }
454 typestr_last = q;
455 }
456
457 char **ret;
458 size_t size;
459 LIST_DETACH(&list, &ret, &size);
460 *sz = (int)size;
461 return ret;
462}
463
465{
466 int api;
467
468#ifdef ENABLE_LTDL
470 fprintf(stderr, "The plugin configuration file:\n\t%s\n", gvc->config_path);
471 if (gvc->config_found)
472 fprintf(stderr, "\t\twas successfully loaded.\n");
473 else
474 fprintf(stderr, "\t\twas not found or not usable. No on-demand plugins.\n");
475 } else {
476 fprintf(stderr, "Demand loading of plugins is disabled.\n");
477 }
478#endif
479
480 for (api = 0; api < (int)ARRAY_SIZE(api_names); api++) {
481 if (gvc->common.verbose >= 2)
482 fprintf(stderr, " %s\t: %s\n", api_names[api], gvplugin_list(gvc, api, ":"));
483 else
484 fprintf(stderr, " %s\t: %s\n", api_names[api], gvplugin_list(gvc, api, "?"));
485 }
486
487}
488
490{
491 Agraph_t *g, *sg, *ssg;
492 Agnode_t *n, *m, *loadimage_n, *renderer_n, *device_n, *textlayout_n, *layout_n;
493 Agedge_t *e;
494 Agsym_t *a;
495 gvplugin_package_t *package;
496 const gvplugin_available_t *pnext;
497 char *p, *q, *lq, *t;
498 int neededge_loadimage, neededge_device;
499
500 g = agopen("G", Agdirected, NULL);
501 agattr_text(g, AGRAPH, "label", "");
502 agattr_text(g, AGRAPH, "rankdir", "");
503 agattr_text(g, AGRAPH, "rank", "");
504 agattr_text(g, AGRAPH, "ranksep", "");
505 agattr_text(g, AGNODE, "label", NODENAME_ESC);
506 agattr_text(g, AGNODE, "shape", "");
507 agattr_text(g, AGNODE, "style", "");
508 agattr_text(g, AGNODE, "width", "");
509 agattr_text(g, AGEDGE, "style", "");
510
511 a = agfindgraphattr(g, "rankdir");
512 agxset(g, a, "LR");
513
514 a = agfindgraphattr(g, "ranksep");
515 agxset(g, a, "2.5");
516
517 a = agfindgraphattr(g, "label");
518 agxset(g, a, "Plugins");
519
520 agxbuf buf = {0};
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 agxbprint(&buf, "cluster_%s", package->name);
525 sg = agsubg(g, agxbuse(&buf), 1);
526 a = agfindgraphattr(sg, "label");
527 agxset(sg, a, package->name);
528 for (size_t api = 0; api < ARRAY_SIZE(api_names); api++) {
529 agxbprint(&buf, "%s_%s", package->name, api_names[api]);
530 ssg = agsubg(sg, agxbuse(&buf), 1);
531 a = agfindgraphattr(ssg, "rank");
532 agxset(ssg, a, "same");
533 for (pnext = gvc->apis[api]; pnext; pnext = pnext->next) {
534 if (pnext->package == package) {
535 t = q = gv_strdup(pnext->typestr);
536 if ((p = strchr(q, ':')))
537 *p++ = '\0';
538 /* Now p = renderer, e.g. "gd"
539 * and q = device, e.g. "png"
540 * or q = loadimage, e.g. "png" */
541 switch (api) {
542 case API_device:
543 case API_loadimage:
544 /* draw device as box - record last device in plugin (if any) in device_n */
545 /* draw loadimage as box - record last loadimage in plugin (if any) in loadimage_n */
546
547 /* hack for aliases */
548 lq = q;
549 if (startswith(q, "jp")) {
550 q = "jpg"; /* canonical - for node name */
551 lq = "jpeg\\njpe\\njpg"; /* list - for label */
552 }
553 else if (startswith(q, "tif")) {
554 q = "tif";
555 lq = "tiff\\ntif";
556 }
557 else if (!strcmp(q, "x11") || !strcmp(q, "xlib")) {
558 q = "x11";
559 lq = "x11\\nxlib";
560 }
561 else if (!strcmp(q, "dot") || !strcmp(q, "gv")) {
562 q = "gv";
563 lq = "gv\\ndot";
564 }
565
566 agxbprint(&buf, "%s_%s_%s", package->name,
567 api_names[api], q);
568 n = agnode(ssg, agxbuse(&buf), 1);
569 a = agfindnodeattr(g, "label");
570 agxset(n, a, lq);
571 a = agfindnodeattr(g, "width");
572 agxset(n, a, "1.0");
573 a = agfindnodeattr(g, "shape");
574 if (api == API_device) {
575 agxset(n, a, "box");
576 device_n = n;
577 }
578 else {
579 agxset(n, a, "box");
580 loadimage_n = n;
581 }
582 if (!(p && *p)) {
583 m = agfindnode(sg, "render_cg");
584 if (!m) {
585 m = agnode(sg, "render_cg", 1);
586 a = agfindgraphattr(g, "label");
587 agxset(m, a, "cg");
588 }
589 agedge(sg, m, n, NULL, 1);
590 }
591 break;
592 case API_render:
593 /* draw renderers as ellipses - record last renderer in plugin (if any) in renderer_n */
594 agxbprint(&buf, "%s_%s", api_names[api], q);
595 renderer_n = n = agnode(ssg, agxbuse(&buf), 1);
596 a = agfindnodeattr(g, "label");
597 agxset(n, a, q);
598 break;
599 case API_textlayout:
600 /* draw textlayout as invtriangle - record last textlayout in plugin (if any) in textlayout_n */
601 /* FIXME? only one textlayout is loaded. Why? */
602 agxbprint(&buf, "%s_%s", api_names[api], q);
603 textlayout_n = n = agnode(ssg, agxbuse(&buf), 1);
604 a = agfindnodeattr(g, "shape");
605 agxset(n, a, "invtriangle");
606 a = agfindnodeattr(g, "label");
607 agxset(n, a, "T");
608 break;
609 case API_layout:
610 /* draw textlayout as hexagon - record last layout in plugin (if any) in layout_n */
611 agxbprint(&buf, "%s_%s", api_names[api], q);
612 layout_n = n = agnode(ssg, agxbuse(&buf), 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 agxbprint(&buf, "%s_%s_invis", package->name, api_names[api]);
629 loadimage_n = n = agnode(ssg, agxbuse(&buf), 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 agxbprint(&buf, "%s_%s_invis_src", package->name,
638 api_names[api]);
639 n = agnode(g, agxbuse(&buf), 1);
640 a = agfindnodeattr(g, "style");
641 agxset(n, a, "invis");
642 a = agfindnodeattr(g, "label");
643 agxset(n, a, "");
644
645 e = agedge(g, n, loadimage_n, NULL, 1);
646 a = agfindedgeattr(g, "style");
647 agxset(e, a, "invis");
648 }
649 if (api == API_render && !renderer_n) {
650 neededge_loadimage = 1;
651 neededge_device = 1;
652 agxbprint(&buf, "%s_%s_invis", package->name, api_names[api]);
653 renderer_n = n = agnode(ssg, agxbuse(&buf), 1);
654 a = agfindnodeattr(g, "style");
655 agxset(n, a, "invis");
656 a = agfindnodeattr(g, "label");
657 agxset(n, a, "");
658 }
659 if (api == API_device && !device_n) {
660 neededge_device = 1;
661 agxbprint(&buf, "%s_%s_invis", package->name, api_names[api]);
662 device_n = n = agnode(ssg, agxbuse(&buf), 1);
663 a = agfindnodeattr(g, "style");
664 agxset(n, a, "invis");
665 a = agfindnodeattr(g, "label");
666 agxset(n, a, "");
667 a = agfindnodeattr(g, "width");
668 agxset(n, a, "1.0");
669 }
670 }
671 if (neededge_loadimage) {
672 e = agedge(sg, loadimage_n, renderer_n, NULL, 1);
673 a = agfindedgeattr(g, "style");
674 agxset(e, a, "invis");
675 }
676 if (neededge_device) {
677 e = agedge(sg, renderer_n, device_n, NULL, 1);
678 a = agfindedgeattr(g, "style");
679 agxset(e, a, "invis");
680 }
681 if (textlayout_n) {
682 e = agedge(sg, loadimage_n, textlayout_n, NULL, 1);
683 a = agfindedgeattr(g, "style");
684 agxset(e, a, "invis");
685 }
686 if (layout_n) {
687 e = agedge(sg, loadimage_n, layout_n, NULL, 1);
688 a = agfindedgeattr(g, "style");
689 agxset(e, a, "invis");
690 }
691 }
692
693 ssg = agsubg(g, "output_formats", 1);
694 a = agfindgraphattr(ssg, "rank");
695 agxset(ssg, a, "same");
696 for (package = gvc->packages; package; package = package->next) {
697 for (size_t api = 0; api < ARRAY_SIZE(api_names); api++) {
698 for (pnext = gvc->apis[api]; pnext; pnext = pnext->next) {
699 if (pnext->package == package) {
700 t = q = gv_strdup(pnext->typestr);
701 if ((p = strchr(q, ':')))
702 *p++ = '\0';
703 /* Now p = renderer, e.g. "gd"
704 * and q = device, e.g. "png"
705 * or q = imageloader, e.g. "png" */
706
707 /* hack for aliases */
708 lq = q;
709 if (startswith(q, "jp")) {
710 q = "jpg"; /* canonical - for node name */
711 lq = "jpeg\\njpe\\njpg"; /* list - for label */
712 }
713 else if (startswith(q, "tif")) {
714 q = "tif";
715 lq = "tiff\\ntif";
716 }
717 else if (!strcmp(q, "x11") || !strcmp(q, "xlib")) {
718 q = "x11";
719 lq = "x11\\nxlib";
720 }
721 else if (!strcmp(q, "dot") || !strcmp(q, "gv")) {
722 q = "gv";
723 lq = "gv\\ndot";
724 }
725
726 switch (api) {
727 case API_device: {
728 agxbprint(&buf, "%s_%s_%s", package->name,
729 api_names[api], q);
730 n = agnode(g, agxbuse(&buf), 1);
731 agxbprint(&buf, "output_%s", q);
732 char *const output = agxbuse(&buf);
733 m = agfindnode(ssg, output);
734 if (!m) {
735 m = agnode(ssg, output, 1);
736 a = agfindnodeattr(g, "label");
737 agxset(m, a, lq);
738 a = agfindnodeattr(g, "shape");
739 agxset(m, a, "note");
740 }
741 e = agfindedge(g, n, m);
742 if (!e)
743 e = agedge(g, n, m, NULL, 1);
744 if (p && *p) {
745 agxbprint(&buf, "render_%s", p);
746 char *const render = agxbuse(&buf);
747 m = agfindnode(ssg, render);
748 if (!m)
749 m = agnode(g, render, 1);
750 e = agfindedge(g, m, n);
751 if (!e)
752 e = agedge(g, m, n, NULL, 1);
753 }
754 break;
755 }
756 case API_loadimage: {
757 agxbprint(&buf, "%s_%s_%s", package->name,
758 api_names[api], q);
759 n = agnode(g, agxbuse(&buf), 1);
760 agxbprint(&buf, "input_%s", q);
761 char *const input = agxbuse(&buf);
762 m = agfindnode(g, input);
763 if (!m) {
764 m = agnode(g, input, 1);
765 a = agfindnodeattr(g, "label");
766 agxset(m, a, lq);
767 a = agfindnodeattr(g, "shape");
768 agxset(m, a, "note");
769 }
770 e = agfindedge(g, m, n);
771 if (!e)
772 e = agedge(g, m, n, NULL, 1);
773 agxbprint(&buf, "render_%s", p);
774 char *const render = agxbuse(&buf);
775 m = agfindnode(g, render);
776 if (!m)
777 m = agnode(g, render, 1);
778 e = agfindedge(g, n, m);
779 if (!e)
780 e = agedge(g, n, m, NULL, 1);
781 break;
782 }
783 default:
784 break;
785 }
786 free(t);
787 }
788 }
789 }
790 }
791
792 agxbfree(&buf);
793 return g;
794}
static void agxbfree(agxbuf *xb)
free any malloced resources
Definition agxbuf.h:77
static int agxbprint(agxbuf *xb, const char *fmt,...)
Printf-style output to an agxbuf.
Definition agxbuf.h:233
static WUR char * agxbuse(agxbuf *xb)
Definition agxbuf.h:306
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:58
expr procedure type
Definition exparse.y:208
static double len(glCompPoint p)
Definition glutils.c:136
void free(void *)
node NULL
Definition grammar.y:181
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:270
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:429
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:464
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:489
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:218
type-generic dynamically expanding list
#define LIST_DETACH(list, datap, sizep)
Definition list.h:434
#define LIST(type)
Definition list.h:55
#define LIST_APPEND(list, item)
Definition list.h:132
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:90