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