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