Graphviz 13.0.0~dev.20241220.2304
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 <cgraph/list.h>
33#include <util/agxbuf.h>
34#include <util/alloc.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 agerrorf("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 FILE *debug) {
252 gvplugin_available_t *pnext, *rv;
253 gvplugin_library_t *library;
256 int i;
257 api_t apidep;
258
259 if (api == API_device || api == API_loadimage)
260 /* api dependencies - FIXME - find better way to code these *s */
261 apidep = API_render;
262 else
263 apidep = api;
264
265 const strview_t reqtyp = strview(str, ':');
266
267 strview_t reqdep = {0};
268
269 strview_t reqpkg = {0};
270
271 if (reqtyp.data[reqtyp.size] == ':') {
272 reqdep = strview(reqtyp.data + reqtyp.size + strlen(":"), ':');
273 if (reqdep.data[reqdep.size] == ':') {
274 reqpkg = strview(reqdep.data + reqdep.size + strlen(":"), '\0');
275 }
276 }
277
278 agxbuf diag = {0}; // diagnostic messages
279
280 /* iterate the linked list of plugins for this api */
281 for (pnext = gvc->apis[api]; pnext; pnext = pnext->next) {
282 const strview_t typ = strview(pnext->typestr, ':');
283
284 strview_t dep = {0};
285 if (typ.data[typ.size] == ':') {
286 dep = strview(typ.data + typ.size + strlen(":"), '\0');
287 }
288
289 if (!strview_eq(typ, reqtyp)) {
290 agxbprint(&diag, "# type \"%.*s\" did not match \"%.*s\"\n",
291 (int)typ.size, typ.data, (int)reqtyp.size, reqtyp.data);
292 continue; /* types empty or mismatched */
293 }
294 if (dep.data && reqdep.data) {
295 if (!strview_eq(dep, reqdep)) {
296 agxbprint(&diag,
297 "# dependencies \"%.*s\" did not match \"%.*s\"\n",
298 (int)dep.size, dep.data, (int)reqdep.size,
299 reqdep.data);
300 continue; /* dependencies not empty, but mismatched */
301 }
302 }
303 if (!reqpkg.data || strview_str_eq(reqpkg, pnext->package->name)) {
304 // found with no packagename constraints, or with required matching packagename
305
306 if (dep.data && apidep != api) // load dependency if needed, continue if can't find
307 if (!gvplugin_load(gvc, apidep, dep.data, debug)) {
308 agxbprint(&diag,
309 "# plugin loading of dependency \"%.*s\" failed\n",
310 (int)dep.size, dep.data);
311 continue;
312 }
313 break;
314 }
315 }
316 rv = pnext;
317
318 if (rv && rv->typeptr == NULL) {
319 library = gvplugin_library_load(gvc, rv->package->path);
320 if (library) {
321
322 /* Now activate the library with real type ptrs */
323 for (apis = library->apis; (types = apis->types); apis++) {
324 for (i = 0; types[i].type; i++) {
325 /* NB. quality is not checked or replaced
326 * in case user has manually edited quality in config */
327 gvplugin_activate(gvc, apis->api, types[i].type, library->packagename, rv->package->path, &types[i]);
328 }
329 }
330 if (gvc->common.verbose >= 1)
331 fprintf(stderr, "Activated plugin library: %s\n", rv->package->path ? rv->package->path : "<builtin>");
332 }
333 }
334
335 /* one last check for successful load */
336 if (rv && rv->typeptr == NULL) {
337 agxbprint(&diag, "# unsuccessful plugin load\n");
338 rv = NULL;
339 }
340
341 if (rv && gvc->common.verbose >= 1)
342 fprintf(stderr, "Using %s: %s:%s\n", api_names[api], rv->typestr, rv->package->name);
343
344 if (debug != NULL) {
345 fputs(agxbuse(&diag), debug);
346 }
347 agxbfree(&diag);
348
349 gvc->api[api] = rv;
350 return rv;
351}
352
353/* assemble a string list of available plugins
354 * non-re-entrant as character store is shared
355 */
356char *gvplugin_list(GVC_t * gvc, api_t api, const char *str)
357{
358 const gvplugin_available_t *pnext, *plugin;
359 char *bp;
360 bool new = true;
361 static agxbuf xb;
362
363 /* check for valid str */
364 if (!str)
365 return NULL;
366
367 /* does str have a :path modifier? */
368 const strview_t strv = strview(str, ':');
369
370 /* point to the beginning of the linked list of plugins for this api */
371 plugin = gvc->apis[api];
372
373 if (strv.data[strv.size] == ':') { /* if str contains a ':', and if we find a match for the type,
374 then just list the alternative paths for the plugin */
375 for (pnext = plugin; pnext; pnext = pnext->next) {
376 const strview_t type = strview(pnext->typestr, ':');
377 // skip duplicates
378 bool already_seen = false;
379 for (const gvplugin_available_t *p = plugin; p != pnext;
380 p = p->next) {
381 already_seen |= strcasecmp(pnext->typestr, p->typestr) == 0 &&
382 strcasecmp(pnext->package->name, p->package->name) == 0;
383 }
384 if (already_seen) {
385 continue;
386 }
387 // list only the matching type, or all types if str is an empty
388 // string or starts with ":"
389 if (strv.size == 0 || strview_case_eq(strv, type)) {
390 /* list each member of the matching type as "type:path" */
391 agxbprint(&xb, " %s:%s", pnext->typestr, pnext->package->name);
392 new = false;
393 }
394 }
395 }
396 if (new) { /* if the type was not found, or if str without ':',
397 then just list available types */
398 strview_t type_last = {0};
399 for (pnext = plugin; pnext; pnext = pnext->next) {
400 /* list only one instance of type */
401 const strview_t type = strview(pnext->typestr, ':');
402 if (!type_last.data || !strview_case_eq(type_last, type)) {
403 /* list it as "type" i.e. w/o ":path" */
404 agxbprint(&xb, " %.*s", (int)type.size, type.data);
405 new = false;
406 }
407 type_last = type;
408 }
409 }
410 if (new)
411 bp = "";
412 else
413 bp = agxbuse(&xb);
414 return bp;
415}
416
417DEFINE_LIST(strs, char*)
418
419/* gvPluginList:
420 * Return list of plugins of type kind.
421 * The size of the list is stored in sz.
422 * The caller is responsible for freeing the storage. This involves
423 * freeing each item, then the list.
424 * Returns NULL on error, or if there are no plugins.
425 * In the former case, sz is unchanged; in the latter, sz = 0.
426 */
427char **gvPluginList(GVC_t *gvc, const char *kind, int *sz) {
428 size_t api;
429 const gvplugin_available_t *pnext, *plugin;
430 strs_t list = {0};
431
432 if (!kind)
433 return NULL;
434 for (api = 0; api < ARRAY_SIZE(api_names); api++) {
435 if (!strcasecmp(kind, api_names[api]))
436 break;
437 }
438 if (api == ARRAY_SIZE(api_names)) {
439 agerrorf("unrecognized api name \"%s\"\n", kind);
440 return NULL;
441 }
442
443 /* point to the beginning of the linked list of plugins for this api */
444 plugin = gvc->apis[api];
445 strview_t typestr_last = {0};
446 for (pnext = plugin; pnext; pnext = pnext->next) {
447 /* list only one instance of type */
448 strview_t q = strview(pnext->typestr, ':');
449 if (!typestr_last.data || !strview_case_eq(typestr_last, q)) {
450 strs_append(&list, strview_str(q));
451 }
452 typestr_last = q;
453 }
454
455 *sz = (int)strs_size(&list);
456 return strs_detach(&list);
457}
458
460{
461 int api;
462
463#ifdef ENABLE_LTDL
465 fprintf(stderr, "The plugin configuration file:\n\t%s\n", gvc->config_path);
466 if (gvc->config_found)
467 fprintf(stderr, "\t\twas successfully loaded.\n");
468 else
469 fprintf(stderr, "\t\twas not found or not usable. No on-demand plugins.\n");
470 } else {
471 fprintf(stderr, "Demand loading of plugins is disabled.\n");
472 }
473#endif
474
475 for (api = 0; api < (int)ARRAY_SIZE(api_names); api++) {
476 if (gvc->common.verbose >= 2)
477 fprintf(stderr, " %s\t: %s\n", api_names[api], gvplugin_list(gvc, api, ":"));
478 else
479 fprintf(stderr, " %s\t: %s\n", api_names[api], gvplugin_list(gvc, api, "?"));
480 }
481
482}
483
485{
486 Agraph_t *g, *sg, *ssg;
487 Agnode_t *n, *m, *loadimage_n, *renderer_n, *device_n, *textlayout_n, *layout_n;
488 Agedge_t *e;
489 Agsym_t *a;
490 gvplugin_package_t *package;
491 const gvplugin_available_t *pnext;
492 char *p, *q, *lq, *t;
493 int neededge_loadimage, neededge_device;
494
495 g = agopen("G", Agdirected, NULL);
496 agattr(g, AGRAPH, "label", "");
497 agattr(g, AGRAPH, "rankdir", "");
498 agattr(g, AGRAPH, "rank", "");
499 agattr(g, AGRAPH, "ranksep", "");
500 agattr(g, AGNODE, "label", NODENAME_ESC);
501 agattr(g, AGNODE, "shape", "");
502 agattr(g, AGNODE, "style", "");
503 agattr(g, AGNODE, "width", "");
504 agattr(g, AGEDGE, "style", "");
505
506 a = agfindgraphattr(g, "rankdir");
507 agxset(g, a, "LR");
508
509 a = agfindgraphattr(g, "ranksep");
510 agxset(g, a, "2.5");
511
512 a = agfindgraphattr(g, "label");
513 agxset(g, a, "Plugins");
514
515 agxbuf buf = {0};
516 for (package = gvc->packages; package; package = package->next) {
517 loadimage_n = renderer_n = device_n = textlayout_n = layout_n = NULL;
518 neededge_loadimage = neededge_device = 0;
519 agxbprint(&buf, "cluster_%s", package->name);
520 sg = agsubg(g, agxbuse(&buf), 1);
521 a = agfindgraphattr(sg, "label");
522 agxset(sg, a, package->name);
523 for (size_t api = 0; api < ARRAY_SIZE(api_names); api++) {
524 agxbprint(&buf, "%s_%s", package->name, api_names[api]);
525 ssg = agsubg(sg, agxbuse(&buf), 1);
526 a = agfindgraphattr(ssg, "rank");
527 agxset(ssg, a, "same");
528 for (pnext = gvc->apis[api]; pnext; pnext = pnext->next) {
529 if (pnext->package == package) {
530 t = q = gv_strdup(pnext->typestr);
531 if ((p = strchr(q, ':')))
532 *p++ = '\0';
533 /* Now p = renderer, e.g. "gd"
534 * and q = device, e.g. "png"
535 * or q = loadimage, e.g. "png" */
536 switch (api) {
537 case API_device:
538 case API_loadimage:
539 /* draw device as box - record last device in plugin (if any) in device_n */
540 /* draw loadimage as box - record last loadimage in plugin (if any) in loadimage_n */
541
542 /* hack for aliases */
543 lq = q;
544 if (startswith(q, "jp")) {
545 q = "jpg"; /* canonical - for node name */
546 lq = "jpeg\\njpe\\njpg"; /* list - for label */
547 }
548 else if (startswith(q, "tif")) {
549 q = "tif";
550 lq = "tiff\\ntif";
551 }
552 else if (!strcmp(q, "x11") || !strcmp(q, "xlib")) {
553 q = "x11";
554 lq = "x11\\nxlib";
555 }
556 else if (!strcmp(q, "dot") || !strcmp(q, "gv")) {
557 q = "gv";
558 lq = "gv\\ndot";
559 }
560
561 agxbprint(&buf, "%s_%s_%s", package->name,
562 api_names[api], q);
563 n = agnode(ssg, agxbuse(&buf), 1);
564 a = agfindnodeattr(g, "label");
565 agxset(n, a, lq);
566 a = agfindnodeattr(g, "width");
567 agxset(n, a, "1.0");
568 a = agfindnodeattr(g, "shape");
569 if (api == API_device) {
570 agxset(n, a, "box");
571 device_n = n;
572 }
573 else {
574 agxset(n, a, "box");
575 loadimage_n = n;
576 }
577 if (!(p && *p)) {
578 m = agfindnode(sg, "render_cg");
579 if (!m) {
580 m = agnode(sg, "render_cg", 1);
581 a = agfindgraphattr(g, "label");
582 agxset(m, a, "cg");
583 }
584 agedge(sg, m, n, NULL, 1);
585 }
586 break;
587 case API_render:
588 /* draw renderers as ellipses - record last renderer in plugin (if any) in renderer_n */
589 agxbprint(&buf, "%s_%s", api_names[api], q);
590 renderer_n = n = agnode(ssg, agxbuse(&buf), 1);
591 a = agfindnodeattr(g, "label");
592 agxset(n, a, q);
593 break;
594 case API_textlayout:
595 /* draw textlayout as invtriangle - record last textlayout in plugin (if any) in textlayout_n */
596 /* FIXME? only one textlayout is loaded. Why? */
597 agxbprint(&buf, "%s_%s", api_names[api], q);
598 textlayout_n = n = agnode(ssg, agxbuse(&buf), 1);
599 a = agfindnodeattr(g, "shape");
600 agxset(n, a, "invtriangle");
601 a = agfindnodeattr(g, "label");
602 agxset(n, a, "T");
603 break;
604 case API_layout:
605 /* draw textlayout as hexagon - record last layout in plugin (if any) in layout_n */
606 agxbprint(&buf, "%s_%s", api_names[api], q);
607 layout_n = n = agnode(ssg, agxbuse(&buf), 1);
608 a = agfindnodeattr(g, "shape");
609 agxset(n, a, "hexagon");
610 a = agfindnodeattr(g, "label");
611 agxset(n, a, q);
612 break;
613 default:
614 break;
615 }
616 free(t);
617 }
618 }
619 // add some invisible nodes (if needed) and invisible edges to
620 // improve layout of cluster
621 if (api == API_loadimage && !loadimage_n) {
622 neededge_loadimage = 1;
623 agxbprint(&buf, "%s_%s_invis", package->name, api_names[api]);
624 loadimage_n = n = agnode(ssg, agxbuse(&buf), 1);
625 a = agfindnodeattr(g, "style");
626 agxset(n, a, "invis");
627 a = agfindnodeattr(g, "label");
628 agxset(n, a, "");
629 a = agfindnodeattr(g, "width");
630 agxset(n, a, "1.0");
631
632 agxbprint(&buf, "%s_%s_invis_src", package->name,
633 api_names[api]);
634 n = agnode(g, agxbuse(&buf), 1);
635 a = agfindnodeattr(g, "style");
636 agxset(n, a, "invis");
637 a = agfindnodeattr(g, "label");
638 agxset(n, a, "");
639
640 e = agedge(g, n, loadimage_n, NULL, 1);
641 a = agfindedgeattr(g, "style");
642 agxset(e, a, "invis");
643 }
644 if (api == API_render && !renderer_n) {
645 neededge_loadimage = 1;
646 neededge_device = 1;
647 agxbprint(&buf, "%s_%s_invis", package->name, api_names[api]);
648 renderer_n = n = agnode(ssg, agxbuse(&buf), 1);
649 a = agfindnodeattr(g, "style");
650 agxset(n, a, "invis");
651 a = agfindnodeattr(g, "label");
652 agxset(n, a, "");
653 }
654 if (api == API_device && !device_n) {
655 neededge_device = 1;
656 agxbprint(&buf, "%s_%s_invis", package->name, api_names[api]);
657 device_n = n = agnode(ssg, agxbuse(&buf), 1);
658 a = agfindnodeattr(g, "style");
659 agxset(n, a, "invis");
660 a = agfindnodeattr(g, "label");
661 agxset(n, a, "");
662 a = agfindnodeattr(g, "width");
663 agxset(n, a, "1.0");
664 }
665 }
666 if (neededge_loadimage) {
667 e = agedge(sg, loadimage_n, renderer_n, NULL, 1);
668 a = agfindedgeattr(g, "style");
669 agxset(e, a, "invis");
670 }
671 if (neededge_device) {
672 e = agedge(sg, renderer_n, device_n, NULL, 1);
673 a = agfindedgeattr(g, "style");
674 agxset(e, a, "invis");
675 }
676 if (textlayout_n) {
677 e = agedge(sg, loadimage_n, textlayout_n, NULL, 1);
678 a = agfindedgeattr(g, "style");
679 agxset(e, a, "invis");
680 }
681 if (layout_n) {
682 e = agedge(sg, loadimage_n, layout_n, NULL, 1);
683 a = agfindedgeattr(g, "style");
684 agxset(e, a, "invis");
685 }
686 }
687
688 ssg = agsubg(g, "output_formats", 1);
689 a = agfindgraphattr(ssg, "rank");
690 agxset(ssg, a, "same");
691 for (package = gvc->packages; package; package = package->next) {
692 for (size_t api = 0; api < ARRAY_SIZE(api_names); api++) {
693 for (pnext = gvc->apis[api]; pnext; pnext = pnext->next) {
694 if (pnext->package == package) {
695 t = q = gv_strdup(pnext->typestr);
696 if ((p = strchr(q, ':')))
697 *p++ = '\0';
698 /* Now p = renderer, e.g. "gd"
699 * and q = device, e.g. "png"
700 * or q = imageloader, e.g. "png" */
701
702 /* hack for aliases */
703 lq = q;
704 if (startswith(q, "jp")) {
705 q = "jpg"; /* canonical - for node name */
706 lq = "jpeg\\njpe\\njpg"; /* list - for label */
707 }
708 else if (startswith(q, "tif")) {
709 q = "tif";
710 lq = "tiff\\ntif";
711 }
712 else if (!strcmp(q, "x11") || !strcmp(q, "xlib")) {
713 q = "x11";
714 lq = "x11\\nxlib";
715 }
716 else if (!strcmp(q, "dot") || !strcmp(q, "gv")) {
717 q = "gv";
718 lq = "gv\\ndot";
719 }
720
721 switch (api) {
722 case API_device: {
723 agxbprint(&buf, "%s_%s_%s", package->name,
724 api_names[api], q);
725 n = agnode(g, agxbuse(&buf), 1);
726 agxbprint(&buf, "output_%s", q);
727 char *const output = agxbuse(&buf);
728 m = agfindnode(ssg, output);
729 if (!m) {
730 m = agnode(ssg, output, 1);
731 a = agfindnodeattr(g, "label");
732 agxset(m, a, lq);
733 a = agfindnodeattr(g, "shape");
734 agxset(m, a, "note");
735 }
736 e = agfindedge(g, n, m);
737 if (!e)
738 e = agedge(g, n, m, NULL, 1);
739 if (p && *p) {
740 agxbprint(&buf, "render_%s", p);
741 char *const render = agxbuse(&buf);
742 m = agfindnode(ssg, render);
743 if (!m)
744 m = agnode(g, render, 1);
745 e = agfindedge(g, m, n);
746 if (!e)
747 e = agedge(g, m, n, NULL, 1);
748 }
749 break;
750 }
751 case API_loadimage: {
752 agxbprint(&buf, "%s_%s_%s", package->name,
753 api_names[api], q);
754 n = agnode(g, agxbuse(&buf), 1);
755 agxbprint(&buf, "input_%s", q);
756 char *const input = agxbuse(&buf);
757 m = agfindnode(g, input);
758 if (!m) {
759 m = agnode(g, input, 1);
760 a = agfindnodeattr(g, "label");
761 agxset(m, a, lq);
762 a = agfindnodeattr(g, "shape");
763 agxset(m, a, "note");
764 }
765 e = agfindedge(g, m, n);
766 if (!e)
767 e = agedge(g, m, n, NULL, 1);
768 agxbprint(&buf, "render_%s", p);
769 char *const render = agxbuse(&buf);
770 m = agfindnode(g, render);
771 if (!m)
772 m = agnode(g, render, 1);
773 e = agfindedge(g, n, m);
774 if (!e)
775 e = agedge(g, n, m, NULL, 1);
776 break;
777 }
778 default:
779 break;
780 }
781 free(t);
782 }
783 }
784 }
785 }
786
787 agxbfree(&buf);
788 return g;
789}
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:338
int agxset(void *obj, Agsym_t *sym, const char *value)
Definition attr.c:478
Agedge_t * agedge(Agraph_t *g, Agnode_t *t, Agnode_t *h, char *name, int createflag)
Definition edge.c:260
#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:284
Agnode_t * agnode(Agraph_t *g, char *name, int createflag)
Definition node.c:145
#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:58
char ** gvPluginList(GVC_t *gvc, const char *kind, int *sz)
Definition gvplugin.c:427
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:459
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:250
Agraph_t * gvplugin_graph(GVC_t *gvc)
Definition gvplugin.c:484
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:356
static gvplugin_api_t apis[]
textitem scanner parser str
Definition htmlparse.y:224
#define DEFINE_LIST(name, type)
Definition list.h:26
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:425
string attribute descriptor symbol in Agattr_s.dict
Definition cgraph.h:637
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