|
Graphviz
2.29.20120524.0446
|
00001 /* $Id$ $Revision$ */ 00002 /* vim:set shiftwidth=4 ts=8: */ 00003 00004 /************************************************************************* 00005 * Copyright (c) 2011 AT&T Intellectual Property 00006 * All rights reserved. This program and the accompanying materials 00007 * are made available under the terms of the Eclipse Public License v1.0 00008 * which accompanies this distribution, and is available at 00009 * http://www.eclipse.org/legal/epl-v10.html 00010 * 00011 * Contributors: See CVS logs. Details at http://www.graphviz.org/ 00012 *************************************************************************/ 00013 00014 #ifdef HAVE_CONFIG_H 00015 #include "config.h" 00016 #endif 00017 00018 #include "gvconfig.h" 00019 00020 #include <string.h> 00021 00022 #ifdef ENABLE_LTDL 00023 #include <sys/types.h> 00024 #ifdef WIN32 00025 #include <windows.h> 00026 #define GLOB_NOSPACE 1 /* Ran out of memory. */ 00027 #define GLOB_ABORTED 2 /* Read error. */ 00028 #define GLOB_NOMATCH 3 /* No matches found. */ 00029 #define GLOB_NOSORT 4 00030 #define DMKEY "Software\\Microsoft" //key to look for library dir 00031 #include "regex_win32.h" 00032 //#include <regex_win32.c> 00033 typedef struct { 00034 int gl_pathc; /* count of total paths so far */ 00035 int gl_matchc; /* count of paths matching pattern */ 00036 int gl_offs; /* reserved at beginning of gl_pathv */ 00037 int gl_flags; /* returned flags */ 00038 char **gl_pathv; /* list of paths matching pattern */ 00039 } glob_t; 00040 static void globfree (glob_t* pglob); 00041 static int glob (GVC_t * gvc, char*, int, int (*errfunc)(const char *, int), glob_t*); 00042 #else 00043 #include <regex.h> 00044 #include <glob.h> 00045 #endif 00046 #include <sys/stat.h> 00047 #ifdef HAVE_UNISTD_H 00048 #include <unistd.h> 00049 #endif 00050 #endif 00051 00052 #ifdef __APPLE__ 00053 #include <mach-o/dyld.h> 00054 #endif 00055 00056 #include "memory.h" 00057 #include "const.h" 00058 #include "types.h" 00059 00060 #include "gvplugin.h" 00061 #include "gvcjob.h" 00062 #include "gvcint.h" 00063 #include "gvcproc.h" 00064 00065 /* 00066 A config for gvrender is a text file containing a 00067 list of plugin librariess and their capabilities using a tcl-like 00068 syntax 00069 00070 Lines beginning with '#' are ignored as comments 00071 00072 Blank lines are allowed and ignored. 00073 00074 plugin_library_path packagename { 00075 plugin_api { 00076 plugin_type plugin_quality 00077 ... 00078 } 00079 ... 00080 ... 00081 00082 e.g. 00083 00084 /usr/lib/graphviz/libgvplugin_cairo.so cairo {renderer {x 0 png 10 ps -10}} 00085 /usr/lib/graphviz/libgvplugin_gd.so gd {renderer {png 0 gif 0 jpg 0}} 00086 00087 Internally the config is maintained as lists of plugin_types for each plugin_api. 00088 If multiple plugins of the same type are found then the highest quality wins. 00089 If equal quality then the last-one-installed wins (thus giving preference to 00090 external plugins over internal builtins). 00091 00092 */ 00093 00094 static gvplugin_package_t * gvplugin_package_record(GVC_t * gvc, char *path, char *name) 00095 { 00096 gvplugin_package_t *package = gmalloc(sizeof(gvplugin_package_t)); 00097 package->path = (path) ? strdup(path) : NULL; 00098 package->name = strdup(name); 00099 package->next = gvc->packages; 00100 gvc->packages = package; 00101 return package; 00102 } 00103 00104 #ifdef ENABLE_LTDL 00105 /* 00106 separator - consume all non-token characters until next token. This includes: 00107 comments: '#' ... '\n' 00108 nesting: '{' 00109 unnesting: '}' 00110 whitespace: ' ','\t','\n' 00111 00112 *nest is changed according to nesting/unnesting processed 00113 */ 00114 static void separator(int *nest, char **tokens) 00115 { 00116 char c, *s; 00117 00118 s = *tokens; 00119 while ((c = *s)) { 00120 /* #->eol = comment */ 00121 if (c == '#') { 00122 s++; 00123 while ((c = *s)) { 00124 s++; 00125 if (c == '\n') 00126 break; 00127 } 00128 continue; 00129 } 00130 if (c == '{') { 00131 (*nest)++; 00132 s++; 00133 continue; 00134 } 00135 if (c == '}') { 00136 (*nest)--; 00137 s++; 00138 continue; 00139 } 00140 if (c == ' ' || c == '\n' || c == '\t') { 00141 s++; 00142 continue; 00143 } 00144 break; 00145 } 00146 *tokens = s; 00147 } 00148 00149 /* 00150 token - capture all characters until next separator, then consume separator, 00151 return captured token, leave **tokens pointing to next token. 00152 */ 00153 static char *token(int *nest, char **tokens) 00154 { 00155 char c, *s, *t; 00156 00157 s = t = *tokens; 00158 while ((c = *s)) { 00159 if (c == '#' 00160 || c == ' ' || c == '\t' || c == '\n' || c == '{' || c == '}') 00161 break; 00162 s++; 00163 } 00164 *tokens = s; 00165 separator(nest, tokens); 00166 *s = '\0'; 00167 return t; 00168 } 00169 00170 static int gvconfig_plugin_install_from_config(GVC_t * gvc, char *s) 00171 { 00172 char *path, *name, *api; 00173 const char *type; 00174 api_t gv_api; 00175 int quality, rc; 00176 int nest = 0; 00177 gvplugin_package_t *package; 00178 00179 separator(&nest, &s); 00180 while (*s) { 00181 path = token(&nest, &s); 00182 if (nest == 0) 00183 name = token(&nest, &s); 00184 else 00185 name = "x"; 00186 package = gvplugin_package_record(gvc, path, name); 00187 do { 00188 api = token(&nest, &s); 00189 gv_api = gvplugin_api(api); 00190 if (gv_api == -1) { 00191 agerr(AGERR, "invalid api in config: %s %s\n", path, api); 00192 return 0; 00193 } 00194 do { 00195 if (nest == 2) { 00196 type = token(&nest, &s); 00197 if (nest == 2) 00198 quality = atoi(token(&nest, &s)); 00199 else 00200 quality = 0; 00201 rc = gvplugin_install (gvc, gv_api, 00202 type, quality, package, NULL); 00203 if (!rc) { 00204 agerr(AGERR, "config error: %s %s %s\n", path, api, type); 00205 return 0; 00206 } 00207 } 00208 } while (nest == 2); 00209 } while (nest == 1); 00210 } 00211 return 1; 00212 } 00213 #endif 00214 00215 void gvconfig_plugin_install_from_library(GVC_t * gvc, char *path, gvplugin_library_t *library) 00216 { 00217 gvplugin_api_t *apis; 00218 gvplugin_installed_t *types; 00219 gvplugin_package_t *package; 00220 int i; 00221 00222 package = gvplugin_package_record(gvc, path, library->packagename); 00223 for (apis = library->apis; (types = apis->types); apis++) { 00224 for (i = 0; types[i].type; i++) { 00225 gvplugin_install(gvc, apis->api, types[i].type, 00226 types[i].quality, package, &types[i]); 00227 } 00228 } 00229 } 00230 00231 static void gvconfig_plugin_install_builtins(GVC_t * gvc) 00232 { 00233 const lt_symlist_t *s; 00234 const char *name; 00235 00236 if (gvc->common.builtins == NULL) return; 00237 00238 for (s = gvc->common.builtins; (name = s->name); s++) 00239 if (name[0] == 'g' && strstr(name, "_LTX_library")) 00240 gvconfig_plugin_install_from_library(gvc, NULL, 00241 (gvplugin_library_t *)(s->address)); 00242 } 00243 00244 #ifdef ENABLE_LTDL 00245 static void gvconfig_write_library_config(GVC_t *gvc, char *path, gvplugin_library_t *library, FILE *f) 00246 { 00247 gvplugin_api_t *apis; 00248 gvplugin_installed_t *types; 00249 int i; 00250 00251 fprintf(f, "%s %s {\n", path, library->packagename); 00252 for (apis = library->apis; (types = apis->types); apis++) { 00253 fprintf(f, "\t%s {\n", gvplugin_api_name(apis->api)); 00254 for (i = 0; types[i].type; i++) { 00255 /* verify that dependencies are available */ 00256 if (! (gvplugin_load(gvc, apis->api, types[i].type))) 00257 fprintf(f, "#FAILS"); 00258 fprintf(f, "\t\t%s %d\n", types[i].type, types[i].quality); 00259 } 00260 fputs ("\t}\n", f); 00261 } 00262 fputs ("}\n", f); 00263 } 00264 00265 #define BSZ 1024 00266 #define DOTLIBS "/.libs" 00267 #define STRLEN(s) (sizeof(s)-1) 00268 00269 char * gvconfig_libdir(GVC_t * gvc) 00270 { 00271 static char line[BSZ]; 00272 static char *libdir; 00273 static boolean dirShown = 0; 00274 char *tmp; 00275 00276 if (!libdir) { 00277 libdir=getenv("GVBINDIR"); 00278 if (!libdir) { 00279 #ifdef WIN32 00280 int r; 00281 char* s; 00282 00283 MEMORY_BASIC_INFORMATION mbi; 00284 if (VirtualQuery (&gvconfig_libdir, &mbi, sizeof(mbi)) == 0) { 00285 agerr(AGERR,"failed to get handle for executable.\n"); 00286 return 0; 00287 } 00288 r = GetModuleFileName ((HMODULE)mbi.AllocationBase, line, BSZ); 00289 if (!r || (r == BSZ)) { 00290 agerr(AGERR,"failed to get path for executable.\n"); 00291 return 0; 00292 } 00293 s = strrchr(line,'\\'); 00294 if (!s) { 00295 agerr(AGERR,"no slash in path %s.\n", line); 00296 return 0; 00297 } 00298 *s = '\0'; 00299 libdir = line; 00300 #else 00301 libdir = GVLIBDIR; 00302 #ifdef __APPLE__ 00303 uint32_t i, c = _dyld_image_count(); 00304 size_t len, ind; 00305 const char* path; 00306 for (i = 0; i < c; ++i) { 00307 path = _dyld_get_image_name(i); 00308 tmp = strstr(path, "/libgvc."); 00309 if (tmp) { 00310 if (tmp > path) { 00311 /* Check for real /lib dir. Don't accept pre-install /.libs */ 00312 char* s = tmp-1; 00313 /* back up to previous slash (or head of string) */ 00314 while ((*s != '/') && (s > path)) s--; 00315 if (strncmp (s, DOTLIBS, STRLEN(DOTLIBS)) == 0); 00316 continue; 00317 } 00318 00319 ind = tmp - path; /* byte offset */ 00320 len = ind + sizeof("/graphviz"); 00321 if (len < BSZ) 00322 libdir = line; 00323 else 00324 libdir = gmalloc(len); 00325 bcopy (path, libdir, ind); 00326 /* plugins are in "graphviz" subdirectory */ 00327 strcpy(libdir+ind, "/graphviz"); 00328 break; 00329 } 00330 } 00331 #else 00332 FILE* f = fopen ("/proc/self/maps", "r"); 00333 char* path; 00334 if (f) { 00335 while (!feof (f)) { 00336 if (!fgets (line, sizeof (line), f)) 00337 continue; 00338 if (!strstr (line, " r-xp ")) 00339 continue; 00340 path = strchr (line, '/'); 00341 if (!path) 00342 continue; 00343 tmp = strstr (path, "/libgvc."); 00344 if (tmp) { 00345 *tmp = 0; 00346 /* Check for real /lib dir. Don't accept pre-install /.libs */ 00347 if (strcmp(strrchr(path,'/'), "/.libs") == 0) 00348 continue; 00349 strcpy(line, path); /* use line buffer for result */ 00350 strcat(line, "/graphviz"); /* plugins are in "graphviz" subdirectory */ 00351 libdir = line; 00352 break; 00353 } 00354 } 00355 fclose (f); 00356 } 00357 #endif 00358 #endif 00359 } 00360 } 00361 if (gvc->common.verbose && !dirShown) { 00362 fprintf (stderr, "libdir = \"%s\"\n", (libdir ? libdir : "<null>")); 00363 dirShown = 1; 00364 } 00365 return libdir; 00366 } 00367 #endif 00368 00369 #ifdef ENABLE_LTDL 00370 static void config_rescan(GVC_t *gvc, char *config_path) 00371 { 00372 FILE *f = NULL; 00373 glob_t globbuf; 00374 char *config_glob, *config_re, *path, *libdir; 00375 int i, rc, re_status; 00376 gvplugin_library_t *library; 00377 regex_t re; 00378 #ifndef WIN32 00379 char *plugin_glob = "libgvplugin_*"; 00380 #endif 00381 #if defined(DARWIN_DYLIB) 00382 char *plugin_re_beg = "[^0-9]\\."; 00383 char *plugin_re_end = "\\.dylib$"; 00384 #elif defined(__MINGW32__) 00385 char *plugin_glob = "libgvplugin_*"; 00386 char *plugin_re_beg = "[^0-9]-"; 00387 char *plugin_re_end = "\\.dll$"; 00388 #elif defined(__CYGWIN__) 00389 plugin_glob = "cyggvplugin_*"; 00390 char *plugin_re_beg = "[^0-9]-"; 00391 char *plugin_re_end = "\\.dll$"; 00392 #elif defined(WIN32) 00393 char *plugin_glob = "gvplugin_*"; 00394 char *plugin_re_beg = "[^0-9]"; 00395 char *plugin_re_end = "\\.dll$"; 00396 #elif ((defined(__hpux__) || defined(__hpux)) && !(defined(__ia64))) 00397 char *plugin_re_beg = "\\.sl\\."; 00398 char *plugin_re_end = "$"; 00399 #else 00400 /* Everyone else */ 00401 char *plugin_re_beg = "\\.so\\."; 00402 char *plugin_re_end= "$"; 00403 #endif 00404 00405 if (config_path) { 00406 f = fopen(config_path,"w"); 00407 if (!f) { 00408 agerr(AGERR,"failed to open %s for write.\n", config_path); 00409 exit(1); 00410 } 00411 00412 fprintf(f, "# This file was generated by \"dot -c\" at time of install.\n\n"); 00413 fprintf(f, "# You may temporarily disable a plugin by removing or commenting out\n"); 00414 fprintf(f, "# a line in this file, or you can modify its \"quality\" value to affect\n"); 00415 fprintf(f, "# default plugin selection.\n\n"); 00416 fprintf(f, "# Manual edits to this file **will be lost** on upgrade.\n\n"); 00417 } 00418 00419 libdir = gvconfig_libdir(gvc); 00420 00421 config_re = gmalloc(strlen(plugin_re_beg) + 20 + strlen(plugin_re_end) + 1); 00422 00423 #if defined(WIN32) && !defined(__MINGW32__) && !defined(__CYGWIN__) 00424 sprintf(config_re,"%s%s", plugin_re_beg, plugin_re_end); 00425 #elif defined(GVPLUGIN_VERSION) 00426 sprintf(config_re,"%s%d%s", plugin_re_beg, GVPLUGIN_VERSION, plugin_re_end); 00427 #else 00428 sprintf(config_re,"%s[0-9]+%s", plugin_re_beg, plugin_re_end); 00429 #endif 00430 00431 if (regcomp(&re, config_re, REG_EXTENDED|REG_NOSUB) != 0) { 00432 agerr(AGERR,"cannot compile regular expression %s", config_re); 00433 } 00434 00435 config_glob = gmalloc(strlen(libdir) + 1 + strlen(plugin_glob) + 1); 00436 strcpy(config_glob, libdir); 00437 strcat(config_glob, DIRSEP); 00438 strcat(config_glob, plugin_glob); 00439 00440 /* load all libraries even if can't save config */ 00441 00442 #if defined(WIN32) 00443 rc = glob(gvc, config_glob, GLOB_NOSORT, NULL, &globbuf); 00444 #else 00445 rc = glob(config_glob, GLOB_NOSORT, NULL, &globbuf); 00446 #endif 00447 if (rc == 0) { 00448 for (i = 0; i < globbuf.gl_pathc; i++) { 00449 re_status = regexec(&re, globbuf.gl_pathv[i], (size_t) 0, NULL, 0); 00450 if (re_status == 0) { 00451 library = gvplugin_library_load(gvc, globbuf.gl_pathv[i]); 00452 if (library) { 00453 gvconfig_plugin_install_from_library(gvc, globbuf.gl_pathv[i], library); 00454 } 00455 } 00456 } 00457 /* rescan with all libs loaded to check cross dependencies */ 00458 for (i = 0; i < globbuf.gl_pathc; i++) { 00459 re_status = regexec(&re, globbuf.gl_pathv[i], (size_t) 0, NULL, 0); 00460 if (re_status == 0) { 00461 library = gvplugin_library_load(gvc, globbuf.gl_pathv[i]); 00462 if (library) { 00463 path = strrchr(globbuf.gl_pathv[i],DIRSEP[0]); 00464 if (path) 00465 path++; 00466 if (f && path) 00467 gvconfig_write_library_config(gvc, path, library, f); 00468 } 00469 } 00470 } 00471 } 00472 regfree(&re); 00473 globfree(&globbuf); 00474 free(config_glob); 00475 free(config_re); 00476 if (f) 00477 fclose(f); 00478 } 00479 #endif 00480 00481 /* 00482 gvconfig - parse a config file and install the identified plugins 00483 */ 00484 void gvconfig(GVC_t * gvc, boolean rescan) 00485 { 00486 #if 0 00487 gvplugin_library_t **libraryp; 00488 #endif 00489 #ifdef ENABLE_LTDL 00490 int sz, rc; 00491 struct stat config_st, libdir_st; 00492 FILE *f = NULL; 00493 char *config_text = NULL; 00494 char *libdir; 00495 char *config_file_name = GVPLUGIN_CONFIG_FILE; 00496 00497 #define MAX_SZ_CONFIG 100000 00498 #endif 00499 00500 /* builtins don't require LTDL */ 00501 gvconfig_plugin_install_builtins(gvc); 00502 00503 gvc->config_found = FALSE; 00504 #ifdef ENABLE_LTDL 00505 if (gvc->common.demand_loading) { 00506 /* see if there are any new plugins */ 00507 libdir = gvconfig_libdir(gvc); 00508 rc = stat(libdir, &libdir_st); 00509 if (rc == -1) { 00510 /* if we fail to stat it then it probably doesn't exist so just fail silently */ 00511 return; 00512 } 00513 00514 if (! gvc->config_path) { 00515 gvc->config_path = gmalloc(strlen(libdir) + 1 + strlen(config_file_name) + 1); 00516 strcpy(gvc->config_path, libdir); 00517 strcat(gvc->config_path, DIRSEP); 00518 strcat(gvc->config_path, config_file_name); 00519 } 00520 00521 if (rescan) { 00522 config_rescan(gvc, gvc->config_path); 00523 gvc->config_found = TRUE; 00524 return; 00525 } 00526 00527 /* load in the cached plugin library data */ 00528 00529 rc = stat(gvc->config_path, &config_st); 00530 if (rc == -1) { 00531 /* silently return without setting gvc->config_found = TRUE */ 00532 return; 00533 } 00534 else if (config_st.st_size > MAX_SZ_CONFIG) { 00535 agerr(AGERR,"%s is bigger than I can handle.\n", gvc->config_path); 00536 } 00537 else { 00538 f = fopen(gvc->config_path,"r"); 00539 if (!f) { 00540 agerr (AGERR,"failed to open %s for read.\n", gvc->config_path); 00541 return; 00542 } 00543 else { 00544 config_text = gmalloc(config_st.st_size + 1); 00545 sz = fread(config_text, 1, config_st.st_size, f); 00546 if (sz == 0) { 00547 agerr(AGERR,"%s is zero sized, or other read error.\n", gvc->config_path); 00548 free(config_text); 00549 } 00550 else { 00551 gvc->config_found = TRUE; 00552 config_text[sz] = '\0'; /* make input into a null terminated string */ 00553 rc = gvconfig_plugin_install_from_config(gvc, config_text); 00554 /* NB. config_text not freed because we retain char* into it */ 00555 } 00556 } 00557 if (f) { 00558 fclose(f); 00559 } 00560 } 00561 } 00562 #endif 00563 gvtextlayout_select(gvc); /* choose best available textlayout plugin immediately */ 00564 } 00565 00566 #ifdef ENABLE_LTDL 00567 #ifdef WIN32 00568 00569 /* Emulating windows glob */ 00570 00571 /* glob: 00572 * Assumes only GLOB_NOSORT flag given. That is, there is no offset, 00573 * and no previous call to glob. 00574 */ 00575 00576 static int 00577 glob (GVC_t* gvc, char* pattern, int flags, int (*errfunc)(const char *, int), glob_t *pglob) 00578 { 00579 char* libdir; 00580 WIN32_FIND_DATA wfd; 00581 HANDLE h; 00582 char** str=0; 00583 int arrsize=0; 00584 int cnt = 0; 00585 00586 pglob->gl_pathc = 0; 00587 pglob->gl_pathv = NULL; 00588 00589 h = FindFirstFile (pattern, &wfd); 00590 if (h == INVALID_HANDLE_VALUE) return GLOB_NOMATCH; 00591 libdir = gvconfig_libdir(gvc); 00592 do { 00593 if (cnt >= arrsize-1) { 00594 arrsize += 512; 00595 if (str) str = (char**)realloc (str, arrsize*sizeof(char*)); 00596 else str = (char**)malloc (arrsize*sizeof(char*)); 00597 if (!str) return GLOB_NOSPACE; 00598 } 00599 str[cnt] = (char*)malloc (strlen(libdir)+1+strlen(wfd.cFileName)+1); 00600 if (!str[cnt]) return GLOB_NOSPACE; 00601 strcpy(str[cnt],libdir); 00602 strcat(str[cnt],DIRSEP); 00603 strcat(str[cnt],wfd.cFileName); 00604 cnt++; 00605 } while (FindNextFile (h, &wfd)); 00606 str[cnt] = 0; 00607 00608 pglob->gl_pathc = cnt; 00609 pglob->gl_pathv = (char**)realloc(str, (cnt+1)*sizeof(char*)); 00610 00611 return 0; 00612 } 00613 00614 static void 00615 globfree (glob_t* pglob) 00616 { 00617 int i; 00618 for (i = 0; i < pglob->gl_pathc; i++) 00619 free (pglob->gl_pathv[i]); 00620 if (pglob->gl_pathv) 00621 free (pglob->gl_pathv); 00622 } 00623 #endif 00624 #endif
1.7.5