Graphviz 15.1.0~dev.20260613.0511
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 <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 const char *const tmp = strstr(p, "/libgvc.");
277 (void) size;
278 if (tmp) {
279 const char *slash;
280 for (slash = tmp - 1; *slash != '/'; --slash);
281 /* Check for real /lib dir. Don't accept pre-install /.libs */
282 if (strncmp(slash, DOTLIBS, (size_t)(tmp - slash)) != 0) {
283 // plugins are in "graphviz" subdirectory
284 snprintf(line, BSZ, "%.*s/graphviz", (int)(tmp - p), p);
285 // use line buffer for result
286 return 1;
287 }
288 }
289 return 0;
290}
291#endif
292
293char * gvconfig_libdir(GVC_t * gvc)
294{
295 static char line[BSZ];
296 static char *libdir;
297 static bool dirShown = false;
298
299 if (!libdir) {
300 libdir=getenv("GVBINDIR");
301 if (!libdir) {
302#ifdef _WIN32
303 int r;
304 char* s;
305
306 MEMORY_BASIC_INFORMATION mbi;
307 if (VirtualQuery (&gvconfig_libdir, &mbi, sizeof(mbi)) == 0) {
308 agerrorf("failed to get handle for executable.\n");
309 return 0;
310 }
311 r = GetModuleFileName ((HMODULE)mbi.AllocationBase, line, BSZ);
312 if (!r || (r == BSZ)) {
313 agerrorf("failed to get path for executable.\n");
314 return 0;
315 }
316 s = strrchr(line,'\\');
317 if (!s) {
318 agerrorf("no slash in path %s.\n", line);
319 return 0;
320 }
321 *s = '\0';
322 libdir = line;
323#else
324 libdir = GVLIBDIR;
325#ifdef __APPLE__
326 uint32_t i, c = _dyld_image_count();
327 size_t len, ind;
328 for (i = 0; i < c; ++i) {
329 const char *p = _dyld_get_image_name(i);
330 const char* tmp = strstr(p, "/libgvc.");
331 if (tmp) {
332 if (tmp > p) {
333 /* Check for real /lib dir. Don't accept pre-install /.libs */
334 const char *s = tmp - 1;
335 /* back up to previous slash (or head of string) */
336 while (*s != '/' && s > p) s--;
337 if (startswith(s, DOTLIBS))
338 continue;
339 }
340
341 ind = tmp - p; // byte offset
342 len = ind + sizeof("/graphviz");
343 if (len < BSZ)
344 libdir = line;
345 else
346 libdir = gv_alloc(len);
347 if (ind > 0) {
348 memmove(libdir, p, ind);
349 }
350 /* plugins are in "graphviz" subdirectory */
351 strcpy(libdir+ind, "/graphviz");
352 break;
353 }
354 }
355#elif defined(HAVE_DL_ITERATE_PHDR)
356 dl_iterate_phdr(line_callback, line);
357 libdir = line;
358#else
359 FILE* f = gv_fopen("/proc/self/maps", "r");
360 if (f) {
361 while (!feof (f)) {
362 if (!fgets (line, sizeof (line), f))
363 continue;
364 if (!strstr (line, " r-xp "))
365 continue;
366 char *p = strchr(line, '/');
367 if (!p)
368 continue;
369 char* tmp = strstr(p, "/libgvc.");
370 if (tmp) {
371 *tmp = 0;
372 /* Check for real /lib dir. Don't accept pre-install /.libs */
373 if (strcmp(strrchr(p, '/'), "/.libs") == 0)
374 continue;
375 memmove(line, p, strlen(p) + 1); // use line buffer for result
376 strcat(line, "/graphviz"); /* plugins are in "graphviz" subdirectory */
377 libdir = line;
378 break;
379 }
380 }
381 fclose (f);
382 }
383#endif
384#endif
385 }
386 }
387 if (gvc->common.verbose && !dirShown) {
388 fprintf (stderr, "libdir = \"%s\"\n", (libdir ? libdir : "<null>"));
389 dirShown = true;
390 }
391 return libdir;
392}
393#endif
394
395#ifdef ENABLE_LTDL
396// does this path look like a Graphviz plugin of our version?
397static bool is_plugin(const char *filepath) {
398
399 if (filepath == NULL) {
400 return false;
401 }
402
403 // shared library suffix to strip before looking for version number
404#if defined(DARWIN_DYLIB)
405 static const char SUFFIX[] = ".dylib";
406#elif defined(__MINGW32__) || defined(__CYGWIN__) || defined(_WIN32)
407 static const char SUFFIX[] = ".dll";
408#else
409 static const char SUFFIX[] = "";
410#endif
411
412 // does this filename end with the expected suffix?
413 size_t len = strlen(filepath);
414 if (len < strlen(SUFFIX)
415 || strcmp(filepath + len - strlen(SUFFIX), SUFFIX) != 0) {
416 return false;
417 }
418 len -= strlen(SUFFIX);
419
420#if defined(_WIN32) && !defined(__MINGW32__) && !defined(__CYGWIN__)
421 // Windows libraries do not have a version in the filename
422
423#elif defined(GVPLUGIN_VERSION)
424 // turn GVPLUGIN_VERSION into a string
425 #define STRINGIZE_(x) #x
426 #define STRINGIZE(x) STRINGIZE_(x)
427 static const char VERSION[] = STRINGIZE(GVPLUGIN_VERSION);
428 #undef STRINGIZE
429 #undef STRINGIZE_
430
431 // does this filename contain the expected version?
432 if (len < strlen(VERSION)
433 || !startswith(filepath + len - strlen(VERSION), VERSION)) {
434 return false;
435 }
436 len -= strlen(VERSION);
437
438#else
439 // does this filename have a version?
440 if (len == 0 || !gv_isdigit(filepath[len - 1])) {
441 return false;
442 }
443 while (len > 0 && gv_isdigit(filepath[len - 1])) {
444 --len;
445 }
446
447#endif
448
449 // ensure the remainder conforms to what we expect of a shared library
450#if defined(DARWIN_DYLIB)
451 if (len < 2 || gv_isdigit(filepath[len - 2]) || filepath[len - 1] != '.') {
452 return false;
453 }
454#elif defined(__MINGW32__) || defined(__CYGWIN__)
455 if (len < 2 || gv_isdigit(filepath[len - 2]) || filepath[len - 1] != '-') {
456 return false;
457 }
458#elif defined(_WIN32)
459 if (len < 1 || gv_isdigit(filepath[len - 1])) {
460 return false;
461 }
462#else
463 static const char SO[] = ".so.";
464 if (len < strlen(SO) || !startswith(filepath + len - strlen(SO), SO)) {
465 return false;
466 }
467#endif
468
469 return true;
470}
471
472static void config_rescan(GVC_t *gvc, char *config_path)
473{
474 FILE *f = NULL;
475 glob_t globbuf;
476 char *libdir;
477 int rc;
478 gvplugin_library_t *library;
479#if defined(DARWIN_DYLIB)
480 char *plugin_glob = "libgvplugin_*";
481#elif defined(__MINGW32__)
482 char *plugin_glob = "libgvplugin_*";
483#elif defined(__CYGWIN__)
484 char *plugin_glob = "cyggvplugin_*";
485#elif defined(_WIN32)
486 char *plugin_glob = "gvplugin_*";
487#else
488 char *plugin_glob = "libgvplugin_*";
489#endif
490
491 if (config_path) {
492 f = gv_fopen(config_path, "w");
493 if (!f) {
494 agerrorf("failed to open %s for write.\n", config_path);
495 graphviz_exit(1);
496 }
497
498 fprintf(f, "# This file was generated by \"dot -c\" at time of install.\n\n");
499 fprintf(f, "# You may temporarily disable a plugin by removing or commenting out\n");
500 fprintf(f, "# a line in this file, or you can modify its \"quality\" value to affect\n");
501 fprintf(f, "# default plugin selection.\n\n");
502 fprintf(f, "# Manual edits to this file **will be lost** on upgrade.\n\n");
503 }
504
505 libdir = gvconfig_libdir(gvc);
506
507 agxbuf config_glob = {0};
508 agxbprint(&config_glob, "%s%c%s", libdir, PATH_SEPARATOR, plugin_glob);
509
510 /* load all libraries even if can't save config */
511
512#if defined(_WIN32)
513 rc = glob(gvc, agxbuse(&config_glob), GLOB_NOSORT, NULL, &globbuf);
514#else
515 rc = glob(agxbuse(&config_glob), 0, NULL, &globbuf);
516#endif
517 if (rc == 0) {
518 for (size_t i = 0; i < globbuf.gl_pathc; i++) {
519 if (is_plugin(globbuf.gl_pathv[i])) {
520 library = gvplugin_library_load(gvc, globbuf.gl_pathv[i]);
521 if (library) {
522 gvconfig_plugin_install_from_library(gvc, globbuf.gl_pathv[i], library);
523 }
524 }
525 }
526 /* rescan with all libs loaded to check cross dependencies */
527 for (size_t i = 0; i < globbuf.gl_pathc; i++) {
528 if (is_plugin(globbuf.gl_pathv[i])) {
529 library = gvplugin_library_load(gvc, globbuf.gl_pathv[i]);
530 if (library) {
531 char *p = strrchr(globbuf.gl_pathv[i], PATH_SEPARATOR);
532 if (p)
533 p++;
534 if (f && p)
535 gvconfig_write_library_config(gvc, p, library, f);
536 }
537 }
538 }
539 }
540 globfree(&globbuf);
541 agxbfree(&config_glob);
542 if (f)
543 fclose(f);
544}
545#endif
546
547/*
548 gvconfig - parse a config file and install the identified plugins
549 */
550void gvconfig(GVC_t * gvc, bool rescan)
551{
552#ifdef ENABLE_LTDL
553 int rc;
554 struct stat config_st;
555 FILE *f = NULL;
556 char *config_text = NULL;
557 char *libdir;
558 char *config_file_name = GVPLUGIN_CONFIG_FILE;
559
560#endif
561
562 /* builtins don't require LTDL */
564
565 gvc->config_found = false;
566#ifdef ENABLE_LTDL
568 /* see if there are any new plugins */
569 libdir = gvconfig_libdir(gvc);
570 if (access(libdir, F_OK) < 0) {
571 /* if we fail to stat it then it probably doesn't exist so just fail silently */
572 goto done;
573 }
574
575 if (! gvc->config_path) {
576 agxbuf xb = {0};
577 agxbprint(&xb, "%s%c%s", libdir, PATH_SEPARATOR, config_file_name);
578 gvc->config_path = agxbdisown(&xb);
579 }
580
581 if (rescan) {
582 config_rescan(gvc, gvc->config_path);
583 gvc->config_found = true;
584 gvtextlayout_select(gvc); /* choose best available textlayout plugin immediately */
585 assert(gvc->textfont_dt != NULL &&
586 "config rescan performed without any prior first scan");
587 return;
588 }
589
590 /* load in the cached plugin library data */
591
592 rc = stat(gvc->config_path, &config_st);
593 if (rc == -1) {
594 /* silently return without setting gvc->config_found = TRUE */
595 goto done;
596 }
597 else {
598 f = gv_fopen(gvc->config_path, "r");
599 if (!f) {
600 agerrorf("failed to open %s for read.\n", gvc->config_path);
601 return;
602 }
603 else if (config_st.st_size == 0) {
604 agerrorf("%s is zero sized.\n", gvc->config_path);
605 }
606 else {
607 config_text = gv_alloc((size_t)config_st.st_size + 1);
608 size_t sz = fread(config_text, 1, (size_t)config_st.st_size, f);
609 if (sz == 0) {
610 agerrorf("%s read error.\n", gvc->config_path);
611 }
612 else {
613 gvc->config_found = true;
614 config_text[sz] = '\0'; /* make input into a null terminated string */
615 rc = gvconfig_plugin_install_from_config(gvc, config_text);
616 }
617 free(config_text);
618 }
619 if (f) {
620 fclose(f);
621 }
622 }
623 }
624done:
625#else
626 (void)rescan;
627#endif
628 gvtextlayout_select(gvc); /* choose best available textlayout plugin immediately */
629 textfont_dict_open(gvc); /* initialize font dict */
630}
631
632#ifdef ENABLE_LTDL
633#ifdef _WIN32
634
635/* Emulating windows glob */
636
637/* glob:
638 * Assumes only GLOB_NOSORT flag given. That is, there is no offset,
639 * and no previous call to glob.
640 */
641
642static int
643glob (GVC_t* gvc, char* pattern, int flags, int (*errfunc)(const char *, int), glob_t *pglob)
644{
645 (void)flags;
646 (void)errfunc;
647
648 char* libdir;
649 WIN32_FIND_DATA wfd;
650 HANDLE h;
651 LIST(char *) strs = {.dtor = LIST_DTOR_FREE};
652
653 pglob->gl_pathc = 0;
654 pglob->gl_pathv = NULL;
655
656 // the Windows APIs use '\'-separated directory components, but MinGW uses
657 // '/' as the canonical directory separator, so convert these
658#ifdef __MINGW32__
659 for (size_t i = 0; pattern[i] != '\0'; ++i) {
660 if (pattern[i] == '/') {
661 pattern[i] = '\\';
662 }
663 }
664#endif
665
666 h = FindFirstFile (pattern, &wfd);
667 if (h == INVALID_HANDLE_VALUE) return GLOB_NOMATCH;
668 libdir = gvconfig_libdir(gvc);
669 do {
670 const size_t size =
671 strlen(libdir) + 1 /* path separator */ + strlen(wfd.cFileName) + 1;
672 char *const entry = malloc(size);
673 if (!entry) {
674 goto oom;
675 }
676 snprintf(entry, size, "%s%c%s", libdir, PATH_SEPARATOR, wfd.cFileName);
677 if (!LIST_TRY_APPEND(&strs, entry)) {
678 free(entry);
679 goto oom;
680 }
681 } while (FindNextFile (h, &wfd));
682 if (!LIST_TRY_APPEND(&strs, NULL) != 0) {
683 goto oom;
684 }
685
686 LIST_DETACH(&strs, &pglob->gl_pathv, &pglob->gl_pathc);
687
688 return 0;
689
690oom:
691 LIST_FREE(&strs);
692 return GLOB_NOSPACE;
693}
694
695static void
696globfree (glob_t* pglob)
697{
698 int i;
699 for (i = 0; i < pglob->gl_pathc; i++)
700 free (pglob->gl_pathv[i]);
701 free (pglob->gl_pathv);
702}
703#endif
704#endif
Dynamically expanding string buffers.
static void agxbfree(agxbuf *xb)
free any malloced resources
Definition agxbuf.h:97
static int agxbprint(agxbuf *xb, const char *fmt,...)
Printf-style output to an agxbuf.
Definition agxbuf.h:252
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:100
void gvconfig(GVC_t *gvc, bool rescan)
Definition gvconfig.c:550
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: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: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:251
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[]
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