Graphviz 13.1.2~dev.20250807.2324
Loading...
Searching...
No Matches
gvconfig.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#ifndef _GNU_SOURCE
14#define _GNU_SOURCE 1
15#endif
16
17#include <assert.h>
18#include <gvc/gvconfig.h>
19#include <stdbool.h>
20#include <stdio.h>
21#include <stdlib.h>
22#include <string.h>
23#include <unistd.h>
24#include <util/agxbuf.h>
25#include <util/alloc.h>
26#include <util/exit.h>
27#include <util/gv_ctype.h>
28#include <util/gv_fopen.h>
29#include <util/list.h>
30#include <util/path.h>
31#include <util/startswith.h>
32
33#ifdef ENABLE_LTDL
34#ifdef HAVE_DL_ITERATE_PHDR
35#include <link.h>
36#endif
37#include <sys/types.h>
38#ifdef _WIN32
39#include <windows.h>
40#define GLOB_NOSPACE 1 /* Ran out of memory. */
41#define GLOB_ABORTED 2 /* Read error. */
42#define GLOB_NOMATCH 3 /* No matches found. */
43#define GLOB_NOSORT 4
44typedef struct {
45 size_t gl_pathc; /* count of total paths so far */
46 char **gl_pathv; /* list of paths matching pattern */
47} glob_t;
48static void globfree (glob_t* pglob);
49static int glob (GVC_t * gvc, char*, int, int (*errfunc)(const char *, int), glob_t*);
50#else
51#include <glob.h>
52#endif
53#include <sys/stat.h>
54#endif
55
56#ifdef __APPLE__
57#include <mach-o/dyld.h>
58#endif
59
60#include <common/const.h>
61#include <common/types.h>
62
63#include <gvc/gvplugin.h>
64#include <gvc/gvcjob.h>
65#include <gvc/gvcint.h>
66#include <gvc/gvcproc.h>
67
68/* FIXME */
69extern void textfont_dict_open(GVC_t *gvc);
70
71/*
72 A config for gvrender is a text file containing a
73 list of plugin libraries and their capabilities using a tcl-like
74 syntax
75
76 Lines beginning with '#' are ignored as comments
77
78 Blank lines are allowed and ignored.
79
80 plugin_library_path packagename {
81 plugin_api {
82 plugin_type plugin_quality
83 ...
84 }
85 ...
86 ...
87
88 e.g.
89
90 /usr/lib/graphviz/libgvplugin_cairo.so cairo {renderer {x 0 png 10 ps -10}}
91 /usr/lib/graphviz/libgvplugin_gd.so gd {renderer {png 0 gif 0 jpg 0}}
92
93 Internally the config is maintained as lists of plugin_types for each plugin_api.
94 If multiple plugins of the same type are found then the highest quality wins.
95 If equal quality then the last-one-installed wins (thus giving preference to
96 external plugins over internal builtins).
97
98 */
99
101 const char *package_path,
102 const char *name) {
103 gvplugin_package_t *package = gv_alloc(sizeof(gvplugin_package_t));
104 package->path = package_path ? gv_strdup(package_path) : NULL;
105 package->name = gv_strdup(name);
106 package->next = gvc->packages;
107 gvc->packages = package;
108 return package;
109}
110
111#ifdef ENABLE_LTDL
112/*
113 separator - consume all non-token characters until next token. This includes:
114 comments: '#' ... '\n'
115 nesting: '{'
116 unnesting: '}'
117 whitespace: ' ','\t','\n'
118
119 *nest is changed according to nesting/unnesting processed
120 */
121static void separator(int *nest, char **tokens)
122{
123 char c, *s;
124
125 s = *tokens;
126 while ((c = *s)) {
127 /* #->eol = comment */
128 if (c == '#') {
129 s++;
130 while ((c = *s)) {
131 s++;
132 if (c == '\n')
133 break;
134 }
135 continue;
136 }
137 if (c == '{') {
138 (*nest)++;
139 s++;
140 continue;
141 }
142 if (c == '}') {
143 (*nest)--;
144 s++;
145 continue;
146 }
147 if (c == ' ' || c == '\n' || c == '\t') {
148 s++;
149 continue;
150 }
151 break;
152 }
153 *tokens = s;
154}
155
156/*
157 token - capture all characters until next separator, then consume separator,
158 return captured token, leave **tokens pointing to next token.
159 */
160static char *token(int *nest, char **tokens)
161{
162 char c, *s, *t;
163
164 s = t = *tokens;
165 while ((c = *s)) {
166 if (c == '#'
167 || c == ' ' || c == '\t' || c == '\n' || c == '{' || c == '}')
168 break;
169 s++;
170 }
171 *tokens = s;
172 separator(nest, tokens);
173 *s = '\0';
174 return t;
175}
176
177static int gvconfig_plugin_install_from_config(GVC_t * gvc, char *s)
178{
179 char *package_path, *name;
180 const char *type;
181 int quality;
182 int nest = 0;
183 gvplugin_package_t *package;
184
185 separator(&nest, &s);
186 while (*s) {
187 package_path = token(&nest, &s);
188 if (nest == 0)
189 name = token(&nest, &s);
190 else
191 name = "x";
192 package = gvplugin_package_record(gvc, package_path, name);
193 do {
194 const char *api = token(&nest, &s);
195 const api_t gv_api = gvplugin_api(api);
196 if (gv_api == (api_t)-1) {
197 agerrorf("config error: %s %s not found\n", package_path, api);
198 return 0;
199 }
200 do {
201 if (nest == 2) {
202 type = token(&nest, &s);
203 if (nest == 2)
204 quality = atoi(token(&nest, &s));
205 else
206 quality = 0;
207 bool rc = gvplugin_install(gvc, gv_api, type, quality, package, NULL);
208 if (!rc) {
209 agerrorf("config error: %s %s %s\n", package_path, api, type);
210 return 0;
211 }
212 }
213 } while (nest == 2);
214 } while (nest == 1);
215 }
216 return 1;
217}
218#endif
219
221 gvplugin_library_t *library) {
224 gvplugin_package_t *package;
225 int i;
226
227 package = gvplugin_package_record(gvc, package_path, library->packagename);
228 for (apis = library->apis; (types = apis->types); apis++) {
229 for (i = 0; types[i].type; i++) {
230 gvplugin_install(gvc, apis->api, types[i].type,
231 types[i].quality, package, &types[i]);
232 }
233 }
234}
235
237{
238 const lt_symlist_t *s;
239 const char *name;
240
241 if (gvc->common.builtins == NULL) return;
242
243 for (s = gvc->common.builtins; (name = s->name); s++)
244 if (name[0] == 'g' && strstr(name, "_LTX_library"))
246}
247
248#ifdef ENABLE_LTDL
249static void gvconfig_write_library_config(GVC_t *gvc, char *lib_path,
250 gvplugin_library_t *library,
251 FILE *f) {
253
254 fprintf(f, "%s %s {\n", lib_path, library->packagename);
255 for (gvplugin_api_t *apis = library->apis; (types = apis->types); apis++) {
256 fprintf(f, "\t%s {\n", gvplugin_api_name(apis->api));
257 for (size_t i = 0; types[i].type; i++) {
258 /* verify that dependencies are available */
259 if (!gvplugin_load(gvc, apis->api, types[i].type,
260 gvc->common.verbose > 0 ? f : NULL))
261 fprintf(f, "#FAILS");
262 fprintf(f, "\t\t%s %d\n", types[i].type, types[i].quality);
263 }
264 fputs ("\t}\n", f);
265 }
266 fputs ("}\n", f);
267}
268
269#define BSZ 1024
270#define DOTLIBS "/.libs"
271
272#ifdef HAVE_DL_ITERATE_PHDR
273static int line_callback(struct dl_phdr_info *info, size_t size, void *line)
274{
275 const char *p = info->dlpi_name;
276 char *tmp = strstr(p, "/libgvc.");
277 (void) size;
278 if (tmp) {
279 *tmp = 0;
280 /* Check for real /lib dir. Don't accept pre-install /.libs */
281 if (strcmp(strrchr(p,'/'), DOTLIBS) != 0) {
282 memmove(line, p, strlen(p) + 1); // use line buffer for result
283 strcat(line, "/graphviz"); /* plugins are in "graphviz" subdirectory */
284 return 1;
285 }
286 }
287 return 0;
288}
289#endif
290
291char * gvconfig_libdir(GVC_t * gvc)
292{
293 static char line[BSZ];
294 static char *libdir;
295 static bool dirShown = false;
296
297 if (!libdir) {
298 libdir=getenv("GVBINDIR");
299 if (!libdir) {
300#ifdef _WIN32
301 int r;
302 char* s;
303
304 MEMORY_BASIC_INFORMATION mbi;
305 if (VirtualQuery (&gvconfig_libdir, &mbi, sizeof(mbi)) == 0) {
306 agerrorf("failed to get handle for executable.\n");
307 return 0;
308 }
309 r = GetModuleFileName ((HMODULE)mbi.AllocationBase, line, BSZ);
310 if (!r || (r == BSZ)) {
311 agerrorf("failed to get path for executable.\n");
312 return 0;
313 }
314 s = strrchr(line,'\\');
315 if (!s) {
316 agerrorf("no slash in path %s.\n", line);
317 return 0;
318 }
319 *s = '\0';
320 libdir = line;
321#else
322 libdir = GVLIBDIR;
323#ifdef __APPLE__
324 uint32_t i, c = _dyld_image_count();
325 size_t len, ind;
326 for (i = 0; i < c; ++i) {
327 const char *p = _dyld_get_image_name(i);
328 const char* tmp = strstr(p, "/libgvc.");
329 if (tmp) {
330 if (tmp > p) {
331 /* Check for real /lib dir. Don't accept pre-install /.libs */
332 const char *s = tmp - 1;
333 /* back up to previous slash (or head of string) */
334 while (*s != '/' && s > p) s--;
335 if (startswith(s, DOTLIBS))
336 continue;
337 }
338
339 ind = tmp - p; // byte offset
340 len = ind + sizeof("/graphviz");
341 if (len < BSZ)
342 libdir = line;
343 else
344 libdir = gv_alloc(len);
345 if (ind > 0) {
346 memmove(libdir, p, ind);
347 }
348 /* plugins are in "graphviz" subdirectory */
349 strcpy(libdir+ind, "/graphviz");
350 break;
351 }
352 }
353#elif defined(HAVE_DL_ITERATE_PHDR)
354 dl_iterate_phdr(line_callback, line);
355 libdir = line;
356#else
357 FILE* f = gv_fopen("/proc/self/maps", "r");
358 if (f) {
359 while (!feof (f)) {
360 if (!fgets (line, sizeof (line), f))
361 continue;
362 if (!strstr (line, " r-xp "))
363 continue;
364 char *p = strchr(line, '/');
365 if (!p)
366 continue;
367 char* tmp = strstr(p, "/libgvc.");
368 if (tmp) {
369 *tmp = 0;
370 /* Check for real /lib dir. Don't accept pre-install /.libs */
371 if (strcmp(strrchr(p, '/'), "/.libs") == 0)
372 continue;
373 memmove(line, p, strlen(p) + 1); // use line buffer for result
374 strcat(line, "/graphviz"); /* plugins are in "graphviz" subdirectory */
375 libdir = line;
376 break;
377 }
378 }
379 fclose (f);
380 }
381#endif
382#endif
383 }
384 }
385 if (gvc->common.verbose && !dirShown) {
386 fprintf (stderr, "libdir = \"%s\"\n", (libdir ? libdir : "<null>"));
387 dirShown = true;
388 }
389 return libdir;
390}
391#endif
392
393#ifdef ENABLE_LTDL
394// does this path look like a Graphviz plugin of our version?
395static bool is_plugin(const char *filepath) {
396
397 if (filepath == NULL) {
398 return false;
399 }
400
401 // shared library suffix to strip before looking for version number
402#if defined(DARWIN_DYLIB)
403 static const char SUFFIX[] = ".dylib";
404#elif defined(__MINGW32__) || defined(__CYGWIN__) || defined(_WIN32)
405 static const char SUFFIX[] = ".dll";
406#else
407 static const char SUFFIX[] = "";
408#endif
409
410 // does this filename end with the expected suffix?
411 size_t len = strlen(filepath);
412 if (len < strlen(SUFFIX)
413 || strcmp(filepath + len - strlen(SUFFIX), SUFFIX) != 0) {
414 return false;
415 }
416 len -= strlen(SUFFIX);
417
418#if defined(_WIN32) && !defined(__MINGW32__) && !defined(__CYGWIN__)
419 // Windows libraries do not have a version in the filename
420
421#elif defined(GVPLUGIN_VERSION)
422 // turn GVPLUGIN_VERSION into a string
423 #define STRINGIZE_(x) #x
424 #define STRINGIZE(x) STRINGIZE_(x)
425 static const char VERSION[] = STRINGIZE(GVPLUGIN_VERSION);
426 #undef STRINGIZE
427 #undef STRINGIZE_
428
429 // does this filename contain the expected version?
430 if (len < strlen(VERSION)
431 || !startswith(filepath + len - strlen(VERSION), VERSION)) {
432 return false;
433 }
434 len -= strlen(VERSION);
435
436#else
437 // does this filename have a version?
438 if (len == 0 || !gv_isdigit(filepath[len - 1])) {
439 return false;
440 }
441 while (len > 0 && gv_isdigit(filepath[len - 1])) {
442 --len;
443 }
444
445#endif
446
447 // ensure the remainder conforms to what we expect of a shared library
448#if defined(DARWIN_DYLIB)
449 if (len < 2 || gv_isdigit(filepath[len - 2]) || filepath[len - 1] != '.') {
450 return false;
451 }
452#elif defined(__MINGW32__) || defined(__CYGWIN__)
453 if (len < 2 || gv_isdigit(filepath[len - 2]) || filepath[len - 1] != '-') {
454 return false;
455 }
456#elif defined(_WIN32)
457 if (len < 1 || gv_isdigit(filepath[len - 1])) {
458 return false;
459 }
460#elif ((defined(__hpux__) || defined(__hpux)) && !(defined(__ia64)))
461 static const char SL[] = ".sl.";
462 if (len < strlen(SL) || !startswith(filepath + len - strlen(SL), SL)) {
463 return false;
464 }
465#else
466 static const char SO[] = ".so.";
467 if (len < strlen(SO) || !startswith(filepath + len - strlen(SO), SO)) {
468 return false;
469 }
470#endif
471
472 return true;
473}
474
475static void config_rescan(GVC_t *gvc, char *config_path)
476{
477 FILE *f = NULL;
478 glob_t globbuf;
479 char *libdir;
480 int rc;
481 gvplugin_library_t *library;
482#if defined(DARWIN_DYLIB)
483 char *plugin_glob = "libgvplugin_*";
484#elif defined(__MINGW32__)
485 char *plugin_glob = "libgvplugin_*";
486#elif defined(__CYGWIN__)
487 char *plugin_glob = "cyggvplugin_*";
488#elif defined(_WIN32)
489 char *plugin_glob = "gvplugin_*";
490#else
491 char *plugin_glob = "libgvplugin_*";
492#endif
493
494 if (config_path) {
495 f = gv_fopen(config_path, "w");
496 if (!f) {
497 agerrorf("failed to open %s for write.\n", config_path);
498 graphviz_exit(1);
499 }
500
501 fprintf(f, "# This file was generated by \"dot -c\" at time of install.\n\n");
502 fprintf(f, "# You may temporarily disable a plugin by removing or commenting out\n");
503 fprintf(f, "# a line in this file, or you can modify its \"quality\" value to affect\n");
504 fprintf(f, "# default plugin selection.\n\n");
505 fprintf(f, "# Manual edits to this file **will be lost** on upgrade.\n\n");
506 }
507
508 libdir = gvconfig_libdir(gvc);
509
510 agxbuf config_glob = {0};
511 agxbprint(&config_glob, "%s%c%s", libdir, PATH_SEPARATOR, plugin_glob);
512
513 /* load all libraries even if can't save config */
514
515#if defined(_WIN32)
516 rc = glob(gvc, agxbuse(&config_glob), GLOB_NOSORT, NULL, &globbuf);
517#else
518 rc = glob(agxbuse(&config_glob), 0, NULL, &globbuf);
519#endif
520 if (rc == 0) {
521 for (size_t i = 0; i < globbuf.gl_pathc; i++) {
522 if (is_plugin(globbuf.gl_pathv[i])) {
523 library = gvplugin_library_load(gvc, globbuf.gl_pathv[i]);
524 if (library) {
525 gvconfig_plugin_install_from_library(gvc, globbuf.gl_pathv[i], library);
526 }
527 }
528 }
529 /* rescan with all libs loaded to check cross dependencies */
530 for (size_t i = 0; i < globbuf.gl_pathc; i++) {
531 if (is_plugin(globbuf.gl_pathv[i])) {
532 library = gvplugin_library_load(gvc, globbuf.gl_pathv[i]);
533 if (library) {
534 char *p = strrchr(globbuf.gl_pathv[i], PATH_SEPARATOR);
535 if (p)
536 p++;
537 if (f && p)
538 gvconfig_write_library_config(gvc, p, library, f);
539 }
540 }
541 }
542 }
543 globfree(&globbuf);
544 agxbfree(&config_glob);
545 if (f)
546 fclose(f);
547}
548#endif
549
550/*
551 gvconfig - parse a config file and install the identified plugins
552 */
553void gvconfig(GVC_t * gvc, bool rescan)
554{
555#ifdef ENABLE_LTDL
556 int rc;
557 struct stat config_st;
558 FILE *f = NULL;
559 char *config_text = NULL;
560 char *libdir;
561 char *config_file_name = GVPLUGIN_CONFIG_FILE;
562
563#endif
564
565 /* builtins don't require LTDL */
567
568 gvc->config_found = false;
569#ifdef ENABLE_LTDL
571 /* see if there are any new plugins */
572 libdir = gvconfig_libdir(gvc);
573 if (access(libdir, F_OK) < 0) {
574 /* if we fail to stat it then it probably doesn't exist so just fail silently */
575 goto done;
576 }
577
578 if (! gvc->config_path) {
579 agxbuf xb = {0};
580 agxbprint(&xb, "%s%c%s", libdir, PATH_SEPARATOR, config_file_name);
581 gvc->config_path = agxbdisown(&xb);
582 }
583
584 if (rescan) {
585 config_rescan(gvc, gvc->config_path);
586 gvc->config_found = true;
587 gvtextlayout_select(gvc); /* choose best available textlayout plugin immediately */
588 assert(gvc->textfont_dt != NULL &&
589 "config rescan performed without any prior first scan");
590 return;
591 }
592
593 /* load in the cached plugin library data */
594
595 rc = stat(gvc->config_path, &config_st);
596 if (rc == -1) {
597 /* silently return without setting gvc->config_found = TRUE */
598 goto done;
599 }
600 else {
601 f = gv_fopen(gvc->config_path, "r");
602 if (!f) {
603 agerrorf("failed to open %s for read.\n", gvc->config_path);
604 return;
605 }
606 else if (config_st.st_size == 0) {
607 agerrorf("%s is zero sized.\n", gvc->config_path);
608 }
609 else {
610 config_text = gv_alloc((size_t)config_st.st_size + 1);
611 size_t sz = fread(config_text, 1, (size_t)config_st.st_size, f);
612 if (sz == 0) {
613 agerrorf("%s read error.\n", gvc->config_path);
614 }
615 else {
616 gvc->config_found = true;
617 config_text[sz] = '\0'; /* make input into a null terminated string */
618 rc = gvconfig_plugin_install_from_config(gvc, config_text);
619 }
620 free(config_text);
621 }
622 if (f) {
623 fclose(f);
624 }
625 }
626 }
627done:
628#else
629 (void)rescan;
630#endif
631 gvtextlayout_select(gvc); /* choose best available textlayout plugin immediately */
632 textfont_dict_open(gvc); /* initialize font dict */
633}
634
635#ifdef ENABLE_LTDL
636#ifdef _WIN32
637
638/* Emulating windows glob */
639
640DEFINE_LIST_WITH_DTOR(strs, char *, free)
641
642/* glob:
643 * Assumes only GLOB_NOSORT flag given. That is, there is no offset,
644 * and no previous call to glob.
645 */
646
647static int
648glob (GVC_t* gvc, char* pattern, int flags, int (*errfunc)(const char *, int), glob_t *pglob)
649{
650 (void)flags;
651 (void)errfunc;
652
653 char* libdir;
654 WIN32_FIND_DATA wfd;
655 HANDLE h;
656 strs_t strs = {0};
657
658 pglob->gl_pathc = 0;
659 pglob->gl_pathv = NULL;
660
661 // the Windows APIs use '\'-separated directory components, but MinGW uses
662 // '/' as the canonical directory separator, so convert these
663#ifdef __MINGW32__
664 for (size_t i = 0; pattern[i] != '\0'; ++i) {
665 if (pattern[i] == '/') {
666 pattern[i] = '\\';
667 }
668 }
669#endif
670
671 h = FindFirstFile (pattern, &wfd);
672 if (h == INVALID_HANDLE_VALUE) return GLOB_NOMATCH;
673 libdir = gvconfig_libdir(gvc);
674 do {
675 const size_t size =
676 strlen(libdir) + 1 /* path separator */ + strlen(wfd.cFileName) + 1;
677 char *const entry = malloc(size);
678 if (!entry) {
679 goto oom;
680 }
681 snprintf(entry, size, "%s%c%s", libdir, PATH_SEPARATOR, wfd.cFileName);
682 if (strs_try_append(&strs, entry) != 0) {
683 free(entry);
684 goto oom;
685 }
686 } while (FindNextFile (h, &wfd));
687 if (strs_try_append(&strs, NULL) != 0) {
688 goto oom;
689 }
690
691 pglob->gl_pathc = strs_size(&strs);
692 pglob->gl_pathv = strs_detach(&strs);
693
694 return 0;
695
696oom:
697 strs_free(&strs);
698 return GLOB_NOSPACE;
699}
700
701static void
702globfree (glob_t* pglob)
703{
704 int i;
705 for (i = 0; i < pglob->gl_pathc; i++)
706 free (pglob->gl_pathv[i]);
707 free (pglob->gl_pathv);
708}
709#endif
710#endif
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
static char * agxbdisown(agxbuf *xb)
Definition agxbuf.h:327
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
static NORETURN void graphviz_exit(int status)
Definition exit.h:23
expr procedure type
Definition exparse.y:206
static int flags
Definition gc.c:61
static double len(glCompPoint p)
Definition glutils.c:136
void * malloc(YYSIZE_T)
void free(void *)
node NULL
Definition grammar.y:180
void agerrorf(const char *fmt,...)
Definition agerror.c:165
static GVC_t * gvc
Definition gv.cpp:23
replacements for ctype.h functions
static bool gv_isdigit(int c)
Definition gv_ctype.h:41
FILE * gv_fopen(const char *filename, const char *mode)
Definition gv_fopen.c:32
wrapper around fopen for internal library usage
api_t
Definition gvcext.h:32
static gvplugin_package_t * gvplugin_package_record(GVC_t *gvc, const char *package_path, const char *name)
Definition gvconfig.c:100
void gvconfig(GVC_t *gvc, bool rescan)
Definition gvconfig.c:553
void textfont_dict_open(GVC_t *gvc)
Definition textspan.c:160
void gvconfig_plugin_install_from_library(GVC_t *gvc, char *package_path, gvplugin_library_t *library)
Definition gvconfig.c:220
static void gvconfig_plugin_install_builtins(GVC_t *gvc)
Definition gvconfig.c:236
char * gvconfig_libdir(GVC_t *gvc)
gvplugin_library_t * gvplugin_library_load(GVC_t *gvc, const char *pathname)
Definition gvplugin.c:154
api_t gvplugin_api(const char *str)
Definition gvplugin.c:51
gvplugin_available_t * gvplugin_load(GVC_t *gvc, api_t api, const char *type, FILE *debug)
Definition gvplugin.c:254
int gvtextlayout_select(GVC_t *gvc)
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
static gvplugin_api_t apis[]
#define BSZ
Definition layout.c:277
#define DEFINE_LIST_WITH_DTOR(name, type, dtor)
Definition list.h:30
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
const lt_symlist_t * builtins
Definition gvcommon.h:31
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
Dt_t * textfont_dt
Definition gvcint.h:108
gvplugin_installed_t * types
Definition gvplugin.h:53
ingroup plugin_api
Definition gvplugin.h:35
const char * type
Definition gvplugin.h:41
gvplugin_api_t * apis
Definition gvplugin.h:59
graphs, nodes and edges info: Agraphinfo_t, Agnodeinfo_t and Agedgeinfo_t
Definition grammar.c:89
char * name
Definition grammar.c:94