/misc/src/release/graphviz-2.18-1/src/graphviz-2.18/lib/gvc/gvconfig.c

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

Generated on Mon Mar 31 19:03:26 2008 for Graphviz by  doxygen 1.5.1