Graphviz 12.0.1~dev.20240715.2254
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 <cgraph/agxbuf.h>
24#include <common/types.h>
25#include <gvc/gvc.h>
26#include <gvc/gvplugin.h>
27#include <gvc/gvcjob.h>
28#include <gvc/gvcint.h>
29#include <gvc/gvcproc.h>
30#include <gvc/gvio.h>
31
32#include <common/const.h>
33#include <cgraph/alloc.h>
34#include <cgraph/list.h>
35#include <cgraph/startswith.h>
36#include <cgraph/strcasecmp.h>
37#include <cgraph/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 // list only the matching type, or all types if str is an empty
378 // string or starts with ":"
379 if (strv.size == 0 || strview_case_eq(strv, type)) {
380 /* list each member of the matching type as "type:path" */
381 agxbprint(&xb, " %s:%s", pnext->typestr, pnext->package->name);
382 new = false;
383 }
384 }
385 }
386 if (new) { /* if the type was not found, or if str without ':',
387 then just list available types */
388 strview_t type_last = {0};
389 for (pnext = plugin; pnext; pnext = pnext->next) {
390 /* list only one instance of type */
391 const strview_t type = strview(pnext->typestr, ':');
392 if (!type_last.data || !strview_case_eq(type_last, type)) {
393 /* list it as "type" i.e. w/o ":path" */
394 agxbprint(&xb, " %.*s", (int)type.size, type.data);
395 new = false;
396 }
397 type_last = type;
398 }
399 }
400 if (new)
401 bp = "";
402 else
403 bp = agxbuse(&xb);
404 return bp;
405}
406
407DEFINE_LIST(strs, char*)
408
409/* gvPluginList:
410 * Return list of plugins of type kind.
411 * The size of the list is stored in sz.
412 * The caller is responsible for freeing the storage. This involves
413 * freeing each item, then the list.
414 * Returns NULL on error, or if there are no plugins.
415 * In the former case, sz is unchanged; in the latter, sz = 0.
416 */
417char **gvPluginList(GVC_t *gvc, const char *kind, int *sz) {
418 size_t api;
419 const gvplugin_available_t *pnext, *plugin;
420 strs_t list = {0};
421
422 if (!kind)
423 return NULL;
424 for (api = 0; api < ARRAY_SIZE(api_names); api++) {
425 if (!strcasecmp(kind, api_names[api]))
426 break;
427 }
428 if (api == ARRAY_SIZE(api_names)) {
429 agerrorf("unrecognized api name \"%s\"\n", kind);
430 return NULL;
431 }
432
433 /* point to the beginning of the linked list of plugins for this api */
434 plugin = gvc->apis[api];
435 strview_t typestr_last = {0};
436 for (pnext = plugin; pnext; pnext = pnext->next) {
437 /* list only one instance of type */
438 strview_t q = strview(pnext->typestr, ':');
439 if (!typestr_last.data || !strview_case_eq(typestr_last, q)) {
440 strs_append(&list, strview_str(q));
441 }
442 typestr_last = q;
443 }
444
445 *sz = (int)strs_size(&list);
446 return strs_detach(&list);
447}
448
450{
451 int api;
452
453#ifdef ENABLE_LTDL
455 fprintf(stderr, "The plugin configuration file:\n\t%s\n", gvc->config_path);
456 if (gvc->config_found)
457 fprintf(stderr, "\t\twas successfully loaded.\n");
458 else
459 fprintf(stderr, "\t\twas not found or not usable. No on-demand plugins.\n");
460 } else {
461 fprintf(stderr, "Demand loading of plugins is disabled.\n");
462 }
463#endif
464
465 for (api = 0; api < (int)ARRAY_SIZE(api_names); api++) {
466 if (gvc->common.verbose >= 2)
467 fprintf(stderr, " %s\t: %s\n", api_names[api], gvplugin_list(gvc, api, ":"));
468 else
469 fprintf(stderr, " %s\t: %s\n", api_names[api], gvplugin_list(gvc, api, "?"));
470 }
471
472}
473
475{
476 Agraph_t *g, *sg, *ssg;
477 Agnode_t *n, *m, *loadimage_n, *renderer_n, *device_n, *textlayout_n, *layout_n;
478 Agedge_t *e;
479 Agsym_t *a;
480 gvplugin_package_t *package;
481 const gvplugin_available_t *pnext;
482 char *p, *q, *lq, *t;
483 int neededge_loadimage, neededge_device;
484
485 g = agopen("G", Agdirected, NULL);
486 agattr(g, AGRAPH, "label", "");
487 agattr(g, AGRAPH, "rankdir", "");
488 agattr(g, AGRAPH, "rank", "");
489 agattr(g, AGRAPH, "ranksep", "");
490 agattr(g, AGNODE, "label", NODENAME_ESC);
491 agattr(g, AGNODE, "shape", "");
492 agattr(g, AGNODE, "style", "");
493 agattr(g, AGNODE, "width", "");
494 agattr(g, AGEDGE, "style", "");
495
496 a = agfindgraphattr(g, "rankdir");
497 agxset(g, a, "LR");
498
499 a = agfindgraphattr(g, "ranksep");
500 agxset(g, a, "2.5");
501
502 a = agfindgraphattr(g, "label");
503 agxset(g, a, "Plugins");
504
505 agxbuf buf = {0};
506 for (package = gvc->packages; package; package = package->next) {
507 loadimage_n = renderer_n = device_n = textlayout_n = layout_n = NULL;
508 neededge_loadimage = neededge_device = 0;
509 agxbprint(&buf, "cluster_%s", package->name);
510 sg = agsubg(g, agxbuse(&buf), 1);
511 a = agfindgraphattr(sg, "label");
512 agxset(sg, a, package->name);
513 for (size_t api = 0; api < ARRAY_SIZE(api_names); api++) {
514 agxbprint(&buf, "%s_%s", package->name, api_names[api]);
515 ssg = agsubg(sg, agxbuse(&buf), 1);
516 a = agfindgraphattr(ssg, "rank");
517 agxset(ssg, a, "same");
518 for (pnext = gvc->apis[api]; pnext; pnext = pnext->next) {
519 if (pnext->package == package) {
520 t = q = gv_strdup(pnext->typestr);
521 if ((p = strchr(q, ':')))
522 *p++ = '\0';
523 /* Now p = renderer, e.g. "gd"
524 * and q = device, e.g. "png"
525 * or q = loadimage, e.g. "png" */
526 switch (api) {
527 case API_device:
528 case API_loadimage:
529 /* draw device as box - record last device in plugin (if any) in device_n */
530 /* draw loadimage as box - record last loadimage in plugin (if any) in loadimage_n */
531
532 /* hack for aliases */
533 lq = q;
534 if (startswith(q, "jp")) {
535 q = "jpg"; /* canonical - for node name */
536 lq = "jpeg\\njpe\\njpg"; /* list - for label */
537 }
538 else if (startswith(q, "tif")) {
539 q = "tif";
540 lq = "tiff\\ntif";
541 }
542 else if (!strcmp(q, "x11") || !strcmp(q, "xlib")) {
543 q = "x11";
544 lq = "x11\\nxlib";
545 }
546 else if (!strcmp(q, "dot") || !strcmp(q, "gv")) {
547 q = "gv";
548 lq = "gv\\ndot";
549 }
550
551 agxbprint(&buf, "%s_%s_%s", package->name,
552 api_names[api], q);
553 n = agnode(ssg, agxbuse(&buf), 1);
554 a = agfindnodeattr(g, "label");
555 agxset(n, a, lq);
556 a = agfindnodeattr(g, "width");
557 agxset(n, a, "1.0");
558 a = agfindnodeattr(g, "shape");
559 if (api == API_device) {
560 agxset(n, a, "box");
561 device_n = n;
562 }
563 else {
564 agxset(n, a, "box");
565 loadimage_n = n;
566 }
567 if (!(p && *p)) {
568 m = agfindnode(sg, "render_cg");
569 if (!m) {
570 m = agnode(sg, "render_cg", 1);
571 a = agfindgraphattr(g, "label");
572 agxset(m, a, "cg");
573 }
574 agedge(sg, m, n, NULL, 1);
575 }
576 break;
577 case API_render:
578 /* draw renderers as ellipses - record last renderer in plugin (if any) in renderer_n */
579 agxbprint(&buf, "%s_%s", api_names[api], q);
580 renderer_n = n = agnode(ssg, agxbuse(&buf), 1);
581 a = agfindnodeattr(g, "label");
582 agxset(n, a, q);
583 break;
584 case API_textlayout:
585 /* draw textlayout as invtriangle - record last textlayout in plugin (if any) in textlayout_n */
586 /* FIXME? only one textlayout is loaded. Why? */
587 agxbprint(&buf, "%s_%s", api_names[api], q);
588 textlayout_n = n = agnode(ssg, agxbuse(&buf), 1);
589 a = agfindnodeattr(g, "shape");
590 agxset(n, a, "invtriangle");
591 a = agfindnodeattr(g, "label");
592 agxset(n, a, "T");
593 break;
594 case API_layout:
595 /* draw textlayout as hexagon - record last layout in plugin (if any) in layout_n */
596 agxbprint(&buf, "%s_%s", api_names[api], q);
597 layout_n = n = agnode(ssg, agxbuse(&buf), 1);
598 a = agfindnodeattr(g, "shape");
599 agxset(n, a, "hexagon");
600 a = agfindnodeattr(g, "label");
601 agxset(n, a, q);
602 break;
603 default:
604 break;
605 }
606 free(t);
607 }
608 }
609 // add some invisible nodes (if needed) and invisible edges to
610 // improve layout of cluster
611 if (api == API_loadimage && !loadimage_n) {
612 neededge_loadimage = 1;
613 agxbprint(&buf, "%s_%s_invis", package->name, api_names[api]);
614 loadimage_n = n = agnode(ssg, agxbuse(&buf), 1);
615 a = agfindnodeattr(g, "style");
616 agxset(n, a, "invis");
617 a = agfindnodeattr(g, "label");
618 agxset(n, a, "");
619 a = agfindnodeattr(g, "width");
620 agxset(n, a, "1.0");
621
622 agxbprint(&buf, "%s_%s_invis_src", package->name,
623 api_names[api]);
624 n = agnode(g, agxbuse(&buf), 1);
625 a = agfindnodeattr(g, "style");
626 agxset(n, a, "invis");
627 a = agfindnodeattr(g, "label");
628 agxset(n, a, "");
629
630 e = agedge(g, n, loadimage_n, NULL, 1);
631 a = agfindedgeattr(g, "style");
632 agxset(e, a, "invis");
633 }
634 if (api == API_render && !renderer_n) {
635 neededge_loadimage = 1;
636 neededge_device = 1;
637 agxbprint(&buf, "%s_%s_invis", package->name, api_names[api]);
638 renderer_n = n = agnode(ssg, agxbuse(&buf), 1);
639 a = agfindnodeattr(g, "style");
640 agxset(n, a, "invis");
641 a = agfindnodeattr(g, "label");
642 agxset(n, a, "");
643 }
644 if (api == API_device && !device_n) {
645 neededge_device = 1;
646 agxbprint(&buf, "%s_%s_invis", package->name, api_names[api]);
647 device_n = n = agnode(ssg, agxbuse(&buf), 1);
648 a = agfindnodeattr(g, "style");
649 agxset(n, a, "invis");
650 a = agfindnodeattr(g, "label");
651 agxset(n, a, "");
652 a = agfindnodeattr(g, "width");
653 agxset(n, a, "1.0");
654 }
655 }
656 if (neededge_loadimage) {
657 e = agedge(sg, loadimage_n, renderer_n, NULL, 1);
658 a = agfindedgeattr(g, "style");
659 agxset(e, a, "invis");
660 }
661 if (neededge_device) {
662 e = agedge(sg, renderer_n, device_n, NULL, 1);
663 a = agfindedgeattr(g, "style");
664 agxset(e, a, "invis");
665 }
666 if (textlayout_n) {
667 e = agedge(sg, loadimage_n, textlayout_n, NULL, 1);
668 a = agfindedgeattr(g, "style");
669 agxset(e, a, "invis");
670 }
671 if (layout_n) {
672 e = agedge(sg, loadimage_n, layout_n, NULL, 1);
673 a = agfindedgeattr(g, "style");
674 agxset(e, a, "invis");
675 }
676 }
677
678 ssg = agsubg(g, "output_formats", 1);
679 a = agfindgraphattr(ssg, "rank");
680 agxset(ssg, a, "same");
681 for (package = gvc->packages; package; package = package->next) {
682 for (size_t api = 0; api < ARRAY_SIZE(api_names); api++) {
683 for (pnext = gvc->apis[api]; pnext; pnext = pnext->next) {
684 if (pnext->package == package) {
685 t = q = gv_strdup(pnext->typestr);
686 if ((p = strchr(q, ':')))
687 *p++ = '\0';
688 /* Now p = renderer, e.g. "gd"
689 * and q = device, e.g. "png"
690 * or q = imageloader, e.g. "png" */
691
692 /* hack for aliases */
693 lq = q;
694 if (startswith(q, "jp")) {
695 q = "jpg"; /* canonical - for node name */
696 lq = "jpeg\\njpe\\njpg"; /* list - for label */
697 }
698 else if (startswith(q, "tif")) {
699 q = "tif";
700 lq = "tiff\\ntif";
701 }
702 else if (!strcmp(q, "x11") || !strcmp(q, "xlib")) {
703 q = "x11";
704 lq = "x11\\nxlib";
705 }
706 else if (!strcmp(q, "dot") || !strcmp(q, "gv")) {
707 q = "gv";
708 lq = "gv\\ndot";
709 }
710
711 switch (api) {
712 case API_device: {
713 agxbprint(&buf, "%s_%s_%s", package->name,
714 api_names[api], q);
715 n = agnode(g, agxbuse(&buf), 1);
716 agxbprint(&buf, "output_%s", q);
717 char *const output = agxbuse(&buf);
718 m = agfindnode(ssg, output);
719 if (!m) {
720 m = agnode(ssg, output, 1);
721 a = agfindnodeattr(g, "label");
722 agxset(m, a, lq);
723 a = agfindnodeattr(g, "shape");
724 agxset(m, a, "note");
725 }
726 e = agfindedge(g, n, m);
727 if (!e)
728 e = agedge(g, n, m, NULL, 1);
729 if (p && *p) {
730 agxbprint(&buf, "render_%s", p);
731 char *const render = agxbuse(&buf);
732 m = agfindnode(ssg, render);
733 if (!m)
734 m = agnode(g, render, 1);
735 e = agfindedge(g, m, n);
736 if (!e)
737 e = agedge(g, m, n, NULL, 1);
738 }
739 break;
740 }
741 case API_loadimage: {
742 agxbprint(&buf, "%s_%s_%s", package->name,
743 api_names[api], q);
744 n = agnode(g, agxbuse(&buf), 1);
745 agxbprint(&buf, "input_%s", q);
746 char *const input = agxbuse(&buf);
747 m = agfindnode(g, input);
748 if (!m) {
749 m = agnode(g, input, 1);
750 a = agfindnodeattr(g, "label");
751 agxset(m, a, lq);
752 a = agfindnodeattr(g, "shape");
753 agxset(m, a, "note");
754 }
755 e = agfindedge(g, m, n);
756 if (!e)
757 e = agedge(g, m, n, NULL, 1);
758 agxbprint(&buf, "render_%s", p);
759 char *const render = agxbuse(&buf);
760 m = agfindnode(g, render);
761 if (!m)
762 m = agnode(g, render, 1);
763 e = agfindedge(g, n, m);
764 if (!e)
765 e = agedge(g, n, m, NULL, 1);
766 break;
767 }
768 default:
769 break;
770 }
771 free(t);
772 }
773 }
774 }
775 }
776
777 agxbfree(&buf);
778 return g;
779}
static void agxbfree(agxbuf *xb)
free any malloced resources
Definition agxbuf.h:77
static size_t agxbput(agxbuf *xb, const char *s)
append string s into xb
Definition agxbuf.h:249
static int agxbprint(agxbuf *xb, const char *fmt,...)
Printf-style output to an agxbuf.
Definition agxbuf.h:213
static char * agxbuse(agxbuf *xb)
Definition agxbuf.h:286
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:211
static double len(glCompPoint p)
Definition glutils.c:150
void free(void *)
node NULL
Definition grammar.y:149
Agsym_t * agattr(Agraph_t *g, int kind, char *name, const char *value)
creates or looks up attributes of a graph
Definition attr.c:341
int agxset(void *obj, Agsym_t *sym, const char *value)
Definition attr.c:481
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:273
Agnode_t * agnode(Agraph_t *g, char *name, int createflag)
Definition node.c:147
#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:57
char ** gvPluginList(GVC_t *gvc, const char *kind, int *sz)
Definition gvplugin.c:417
bool render(Agraph_t *g)
Definition gv.cpp:630
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)
static Agdesc_t kind
Definition gvpack.cpp:88
void gvplugin_write_status(GVC_t *gvc)
Definition gvplugin.c:449
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:474
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[]
GVC_t * gvc
Definition htmlparse.c:99
agxbuf * str
Definition htmlparse.c:97
#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:639
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:57
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