libgeda

s_clib.c

Go to the documentation of this file.
00001 /* gEDA - GPL Electronic Design Automation
00002  * libgeda - gEDA's library
00003  * Copyright (C) 1998-2010 gEDA Contributors (see ChangeLog for details)
00004  *
00005  * This program is free software; you can redistribute it and/or modify
00006  * it under the terms of the GNU General Public License as published by
00007  * the Free Software Foundation; either version 2 of the License, or
00008  * (at your option) any later version.
00009  *
00010  * This program is distributed in the hope that it will be useful,
00011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013  * GNU General Public License for more details.
00014  *
00015  * You should have received a copy of the GNU General Public License
00016  * along with this program; if not, write to the Free Software
00017  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
00018  */
00019 
00113 #include <config.h>
00114 #include <missing.h>
00115 
00116 #include <stdio.h>
00117 #include <glib.h>
00118 
00119 #ifdef HAVE_STRING_H
00120 #include <string.h>
00121 #endif
00122 
00123 #ifdef HAVE_LIBDMALLOC
00124 #include <dmalloc.h>
00125 #endif
00126 
00127 #ifdef HAVE_SYS_WAIT_H
00128 #include <sys/wait.h>
00129 #else
00130 #define WIFSIGNALED(x) 0
00131 #define WTERMSIG(x)    0
00132 #define WIFEXITED(x)   1
00133 #define WEXITSTATUS(x) 0
00134 #endif
00135 
00136 #include <time.h>
00137 
00138 #include "libgeda_priv.h"
00139 
00140 /* Constant definitions
00141  * ===================
00142  */
00143 
00146 #define SYM_FILENAME_FILTER ".sym"
00147 
00149 #define CLIB_LIST_CMD       "list"
00150 
00152 #define CLIB_DATA_CMD       "get"
00153 
00155 #define CLIB_MAX_SYMBOL_CACHE 128
00156 
00157 #define CLIB_MIN_SYMBOL_CACHE 96
00158 
00159 /* Type definitions
00160  * ================
00161  */
00162 
00164 enum CLibSourceType { 
00165   CLIB_NONE = 0,
00167   CLIB_DIR, 
00169   CLIB_CMD,
00171   CLIB_SCM,
00172 };
00173 
00175 struct _CLibSource {
00177   enum CLibSourceType type;
00179   gchar *name;
00181   GList *symbols;
00182 
00184   gchar *directory;
00185 
00187   gchar *list_cmd;
00189   gchar *get_cmd;
00190 
00192   SCM list_fn;
00194   SCM get_fn;
00195 };
00196 
00198 struct _CLibSymbol {
00200   CLibSource *source;
00202   gchar *name;
00203 };
00204 
00206 typedef struct _CacheEntry CacheEntry;
00207 struct _CacheEntry {
00209   CLibSymbol *ptr;
00211   gchar *data;
00213   time_t accessed;
00214 };
00215 
00216 /* Static variables
00217  * ================
00218  */
00219 
00221 static GList *clib_sources = NULL;
00222 
00226 static GHashTable *clib_search_cache = NULL;
00227 
00231 static GHashTable *clib_symbol_cache = NULL;
00232 
00233 /* Local static functions
00234  * ======================
00235  */
00236 static void free_symbol (gpointer data, gpointer user_data);
00237 static void free_symbol_cache_entry (gpointer data);
00238 static void free_source (gpointer data, gpointer user_data);
00239 static gint compare_source_name (gconstpointer a, gconstpointer b);
00240 static gint compare_symbol_name (gconstpointer a, gconstpointer b);
00241 static void cache_find_oldest (gpointer key, gpointer value, gpointer user_data);
00242 static gchar *run_source_command (const gchar *command);
00243 static CLibSymbol *source_has_symbol (const CLibSource *source, 
00244                       const gchar *name);
00245 static gchar *uniquify_source_name (const gchar *name);
00246 static void refresh_directory (CLibSource *source);
00247 static void refresh_command (CLibSource *source);
00248 static void refresh_scm (CLibSource *source);
00249 static gchar *get_data_directory (const CLibSymbol *symbol);
00250 static gchar *get_data_command (const CLibSymbol *symbol);
00251 static gchar *get_data_scm (const CLibSymbol *symbol);
00252 
00260 void s_clib_init ()
00261 {
00262   if (clib_sources != NULL) {
00263     s_clib_free ();
00264   }
00265 
00266   if (clib_search_cache != NULL) {
00267     s_clib_flush_search_cache();
00268   } else {
00269     clib_search_cache = g_hash_table_new_full ((GHashFunc) g_str_hash,
00270                                                (GEqualFunc)g_str_equal,
00271                                                (GDestroyNotify) g_free,
00272                                                (GDestroyNotify) g_list_free);
00273   }
00274 
00275   if (clib_symbol_cache != NULL) {
00276     s_clib_flush_symbol_cache();
00277   } else {
00278     clib_symbol_cache =
00279       g_hash_table_new_full ((GHashFunc) g_direct_hash,
00280                              (GEqualFunc) g_direct_equal,
00281                              NULL,
00282                              (GDestroyNotify) free_symbol_cache_entry);
00283   }
00284 }
00285 
00290 static void free_symbol (gpointer data, gpointer user_data)
00291 {
00292   CLibSymbol *symbol = data;
00293   if (symbol != NULL) {
00294     if (symbol->source != NULL) {
00295       symbol->source = NULL;
00296     }
00297     if (symbol->name != NULL) {
00298       g_free (symbol->name);
00299       symbol->name = NULL;
00300     }
00301     g_free(symbol);
00302   }
00303 }
00304 
00309 static void free_symbol_cache_entry (gpointer data)
00310 {
00311   CacheEntry *entry = data;
00312   g_return_if_fail (entry != NULL);
00313   g_free (entry->data);
00314   g_free (entry);
00315 }
00316 
00321 static void free_source (gpointer data, gpointer user_data)
00322 {
00323   CLibSource *source = data;
00324   if (source != NULL) {
00325     if (source->name != NULL) {
00326       g_free (source->name);
00327       source->name = NULL;
00328     }
00329     if (source->symbols != NULL) {
00330       g_list_foreach (source->symbols, (GFunc) free_symbol, NULL);
00331       g_list_free (source->symbols);
00332       source->symbols = NULL;
00333     }
00334     if (source->directory != NULL) {
00335       g_free (source->directory);
00336       source->directory = NULL;
00337     }
00338     if (source->list_cmd != NULL) {
00339       g_free (source->list_cmd);
00340       source->list_cmd = NULL;
00341     }
00342     if (source->get_cmd != NULL) {
00343       g_free (source->get_cmd);
00344       source->get_cmd = NULL;
00345     }
00346     if (source->type == CLIB_SCM) {
00347       scm_gc_unprotect_object (source->list_fn);
00348       scm_gc_unprotect_object (source->get_fn);
00349     }
00350   }
00351 }
00352 
00358 void s_clib_free ()
00359 {
00360   if (clib_sources != NULL) {
00361     g_list_foreach (clib_sources, (GFunc) free_source, NULL);
00362     g_list_free (clib_sources);
00363     clib_sources = NULL;
00364   }
00365 }
00366 
00378 static gint compare_source_name (gconstpointer a, gconstpointer b)
00379 {
00380   const CLibSource *src1 = a;
00381   const CLibSource *src2 = b;
00382 
00383   g_return_val_if_fail ((src1 != NULL), 0);
00384   g_return_val_if_fail ((src2 != NULL), 0);
00385 
00386   g_return_val_if_fail ((src1->name != NULL), 0);
00387   g_return_val_if_fail ((src2->name != NULL), 0);
00388 
00389   return strcasecmp(src1->name, src2->name);
00390 }
00391 
00403 static gint compare_symbol_name (gconstpointer a, gconstpointer b)
00404 {
00405   const CLibSymbol *sym1 = a;
00406   const CLibSymbol *sym2 = b;
00407 
00408   g_return_val_if_fail ((sym1 != NULL), 0);
00409   g_return_val_if_fail ((sym2 != NULL), 0);
00410 
00411   g_return_val_if_fail ((sym1->name != NULL), 0);
00412   g_return_val_if_fail ((sym2->name != NULL), 0);
00413 
00414   return strcasecmp(sym1->name, sym2->name);
00415 }
00416 
00421 static void cache_find_oldest (gpointer key,
00422                                gpointer value,
00423                                gpointer user_data)
00424 {
00425   CacheEntry *current = value;
00426   CacheEntry **oldest = user_data;
00427 
00428   if (current->accessed < (*oldest)->accessed) {
00429     *oldest = current;
00430   }
00431 }
00432 
00448 static gchar *run_source_command (const gchar *command)
00449 {
00450   gchar *standard_output = NULL;
00451   gchar *standard_error = NULL;
00452   gint exit_status;
00453   GError *e = NULL;
00454   gboolean success = FALSE;
00455 
00456   g_return_val_if_fail((command != NULL), NULL);
00457 
00458   g_spawn_command_line_sync (command,
00459                              &standard_output,
00460                              &standard_error,
00461                              &exit_status,
00462                              &e);
00463 
00464   if (e != NULL) {
00465     s_log_message (_("Library command failed [%s]: %s\n"), command,
00466            e->message);
00467     g_error_free (e);
00468 
00469   } else if (WIFSIGNALED(exit_status)) {
00470     s_log_message (_("Library command failed [%s]: Uncaught signal %i.\n"),
00471                    command, WTERMSIG(exit_status));
00472     
00473   } else if (WIFEXITED(exit_status) && WEXITSTATUS(exit_status)) {
00474     s_log_message (_("Library command failed [%s]\n"), command);
00475     s_log_message(_("Error output was:\n%s\n"), standard_error);
00476 
00477   } else {
00478     success = TRUE;
00479   }
00480 
00481   /* forward library command messages */
00482   if (success && standard_error != NULL)
00483     s_log_message ("%s", standard_error);
00484 
00485   g_free (standard_error);
00486   
00487   if (success) return standard_output;
00488 
00489   g_free (standard_output);
00490   return NULL;
00491 }
00492 
00501 GList *s_clib_get_sources (const gboolean sorted)
00502 {
00503   GList *l = g_list_copy(clib_sources);
00504   if (sorted) {
00505     l = g_list_sort (l, (GCompareFunc) compare_source_name);
00506   }
00507   return l;
00508 }
00509 
00520 static CLibSymbol *source_has_symbol (const CLibSource *source, 
00521                       const gchar *name)
00522 {
00523   GList *symlist;
00524   CLibSymbol *symbol;
00525 
00526   for (symlist = g_list_first(source->symbols); 
00527        symlist != NULL; 
00528        symlist = g_list_next(symlist)) {
00529     
00530     symbol = (CLibSymbol *) symlist->data;
00531 
00532     if (strcmp (symbol->name, name) == 0) return symbol;
00533   }
00534 
00535   return NULL;
00536 }
00537 
00546 static gchar *uniquify_source_name (const gchar *name)
00547 {
00548   gchar *newname = NULL;
00549   gint i = 0;
00550 
00551   if (s_clib_get_source_by_name (name) == NULL) {
00552     return g_strdup (name);
00553   }
00554 
00555   do {
00556     g_free (newname);
00557     i++;
00558     newname = g_strdup_printf ("%s<%i>", name, i);
00559   } while (s_clib_get_source_by_name (newname) != NULL);
00560 
00561   s_log_message (_("Library name [%s] already in use.  Using [%s].\n"),
00562                  name, newname);
00563 
00564   return newname;
00565 }
00566 
00576 static void refresh_directory (CLibSource *source)
00577 {
00578   CLibSymbol *symbol;
00579   GDir *dir;
00580   const gchar *entry;
00581   gchar *low_entry;
00582   gchar *fullpath;
00583   gboolean isfile;
00584   GError *e = NULL;
00585 
00586   g_return_if_fail (source != NULL);
00587   g_return_if_fail (source->type == CLIB_DIR);
00588 
00589   /* Clear the current symbol list */
00590   g_list_foreach (source->symbols, (GFunc) free_symbol, NULL);
00591   g_list_free (source->symbols);
00592   source->symbols = NULL;  
00593 
00594   /* Open the directory for reading. */
00595   dir = g_dir_open (source->directory, 0, &e);
00596 
00597   if (e != NULL) {
00598     s_log_message (_("Failed to open directory [%s]: %s\n"),
00599            source->directory, e->message);
00600     g_error_free (e);
00601     return;
00602   }
00603 
00604   while ((entry = g_dir_read_name (dir)) != NULL) {
00605     /* skip ".", ".." & hidden files */
00606     if (entry[0] == '.') continue;
00607 
00608     /* skip subdirectories (for now) */
00609     fullpath = g_build_filename (source->directory, entry, NULL);
00610     isfile = g_file_test (fullpath, G_FILE_TEST_IS_REGULAR);
00611     g_free (fullpath);
00612     if (!isfile) continue;
00613 
00614     /* skip filenames that we already know about. */
00615     if (source_has_symbol (source, entry) != NULL) continue;
00616     
00617     /* skip filenames which don't have the right suffix. */
00618     low_entry = g_utf8_strdown (entry, -1);
00619     if (!g_str_has_suffix (low_entry, SYM_FILENAME_FILTER)) {
00620       g_free (low_entry);
00621       continue;
00622     }
00623     g_free (low_entry);
00624 
00625     /* Create and add new symbol record */
00626     symbol = g_new0 (CLibSymbol, 1);
00627     symbol->source = source;
00628     symbol->name = g_strdup(entry);
00629 
00630     /* Prepend because it's faster and it doesn't matter what order we
00631      * add them. */
00632     source->symbols = g_list_prepend (source->symbols, symbol);
00633   }
00634 
00635   entry = NULL;
00636   g_dir_close (dir);
00637 
00638   /* Now sort the list of symbols by name. */
00639   source->symbols = g_list_sort (source->symbols, 
00640                  (GCompareFunc) compare_symbol_name);
00641 
00642   s_clib_flush_search_cache();
00643   s_clib_flush_symbol_cache();
00644 }
00645 
00653 static void refresh_command (CLibSource *source)
00654 {
00655   gchar *cmdout;
00656   TextBuffer *tb;
00657   const gchar *line;
00658   CLibSymbol *symbol;
00659   gchar *name;
00660 
00661   g_return_if_fail (source != NULL);
00662   g_return_if_fail (source->type == CLIB_CMD);
00663 
00664   /* Clear the current symbol list */
00665   g_list_foreach (source->symbols, (GFunc) free_symbol, NULL);
00666   g_list_free (source->symbols);
00667   source->symbols = NULL;  
00668 
00669   /* Run the command to get the list of symbols */
00670   cmdout = run_source_command (source->list_cmd);
00671   if (cmdout == NULL) return;
00672 
00673   /* Use a TextBuffer to help reading out the lines of the output */
00674   tb = s_textbuffer_new (cmdout, -1);
00675 
00676   while (1) {
00677     line = s_textbuffer_next_line (tb);
00678     if (line == NULL) break;
00679     if (line[0] == '.') continue;  /* TODO is this sane? */
00680 
00681     name = remove_nl(g_strdup(line));
00682 
00683     /* skip symbols already known about */
00684     if (source_has_symbol (source, name) != NULL) {
00685       g_free (name);
00686       continue;
00687     }
00688 
00689     symbol = g_new0 (CLibSymbol, 1);
00690     symbol->source = source;
00691     symbol->name = name;
00692 
00693     /* Prepend because it's faster and it doesn't matter what order we
00694      * add them. */
00695     source->symbols = g_list_prepend (source->symbols, symbol);    
00696   }
00697 
00698   s_textbuffer_free (tb);
00699   g_free (cmdout);
00700 
00701   /* Sort all symbols by name. */
00702   source->symbols = g_list_sort (source->symbols, 
00703                  (GCompareFunc) compare_symbol_name);
00704 
00705   s_clib_flush_search_cache();
00706   s_clib_flush_symbol_cache();
00707 }
00708 
00716 static void refresh_scm (CLibSource *source)
00717 {
00718   SCM symlist;
00719   SCM symname;
00720   CLibSymbol *symbol;
00721   char *tmp;
00722 
00723   g_return_if_fail (source != NULL);
00724   g_return_if_fail (source->type == CLIB_SCM);
00725 
00726   /* Clear the current symbol list */
00727   g_list_foreach (source->symbols, (GFunc) free_symbol, NULL);
00728   g_list_free (source->symbols);
00729   source->symbols = NULL;
00730 
00731   symlist = scm_call_0 (source->list_fn);
00732 
00733   if (SCM_NCONSP (symlist) && (symlist != SCM_EOL)) {
00734     s_log_message (_("Failed to scan library [%s]: Scheme function returned non-list\n"),
00735            source->name);
00736     return;
00737   }
00738 
00739   while (symlist != SCM_EOL) {
00740     symname = SCM_CAR (symlist);
00741     if (!scm_is_string (symname)) {
00742       s_log_message (_("Non-string symbol name while scanning library [%s]\n"),
00743              source->name);
00744     } else {
00745       symbol = g_new0 (CLibSymbol, 1);
00746       symbol->source = source;
00747 
00748       /* Need to make sure that the correct free() function is called
00749        * on strings allocated by Guile. */
00750       tmp = scm_to_utf8_string (symname);
00751       symbol->name = g_strdup(tmp);
00752       free (tmp);
00753 
00754       /* Prepend because it's faster and it doesn't matter what order we
00755        * add them. */
00756       source->symbols = g_list_prepend (source->symbols, symbol);
00757     }
00758  
00759     symlist = SCM_CDR (symlist);
00760   }
00761 
00762   /* Now sort the list of symbols by name. */
00763   source->symbols = g_list_sort (source->symbols, 
00764                  (GCompareFunc) compare_symbol_name);
00765 
00766   s_clib_flush_search_cache();
00767   s_clib_flush_symbol_cache();
00768 }
00769 
00779 void s_clib_refresh ()
00780 {
00781   GList *sourcelist;
00782   CLibSource *source;
00783 
00784   for (sourcelist = clib_sources; 
00785        sourcelist != NULL; 
00786        sourcelist = g_list_next(sourcelist)) {
00787     
00788     source = (CLibSource *) sourcelist->data;
00789     switch (source->type)
00790       {
00791       case CLIB_DIR:
00792     refresh_directory(source);
00793     break;
00794       case CLIB_CMD:
00795     refresh_command (source);
00796     break;
00797       case CLIB_SCM:
00798     refresh_scm (source);
00799     break;
00800       default:
00801     g_critical("s_clib_refresh: source %p has bad source type %i\n",
00802                    source, (gint) source->type);
00803         break;
00804       }
00805   }
00806 }
00807 
00817 const CLibSource *s_clib_get_source_by_name (const gchar *name)
00818 {
00819   GList *sourcelist;
00820   CLibSource *source;
00821 
00822   for (sourcelist = clib_sources; 
00823        sourcelist != NULL; 
00824        sourcelist = g_list_next(sourcelist)) {
00825 
00826     source = (CLibSource *) sourcelist->data;
00827     if (strcmp (source->name, name) == 0) {
00828       return source;
00829     }
00830   }
00831 
00832   return NULL;
00833 }
00834 
00847 const CLibSource *s_clib_add_directory (const gchar *directory, 
00848                     const gchar *name)
00849 {
00850   CLibSource *source;
00851   gchar *intname, *realname;
00852 
00853   if (directory == NULL) {
00854     return NULL;
00855   }
00856   
00857   if (name == NULL) {
00858     intname = g_path_get_basename (directory);
00859     realname = uniquify_source_name (intname);
00860     g_free (intname);
00861   } else {
00862     realname = uniquify_source_name (name);
00863   }  
00864 
00865   source = g_new0 (CLibSource, 1);
00866   source->type = CLIB_DIR;
00867   source->directory = g_strdup (directory);
00868   source->name = realname;
00869 
00870   refresh_directory (source);
00871 
00872   /* Sources added later get scanned earlier */
00873   clib_sources = g_list_prepend (clib_sources, source);
00874 
00875   return source;
00876 }
00877 
00893 const CLibSource *s_clib_add_command (const gchar *list_cmd,
00894                                       const gchar *get_cmd,
00895                       const gchar *name)
00896 {
00897   CLibSource *source;
00898   gchar *realname;
00899   
00900   if (name == NULL) {
00901     s_log_message (_("Cannot add library: name not specified\n"));
00902     return NULL;
00903   }
00904   
00905   realname = uniquify_source_name (name);
00906 
00907   if (list_cmd == NULL || get_cmd == NULL) {
00908     s_log_message (_("Cannot add library [%s]: both 'list' and "
00909                      "'get' commands must be specified.\n"),
00910            realname);
00911   }
00912 
00913   source = g_new0 (CLibSource, 1);
00914   source->type = CLIB_CMD;
00915   source->name = realname;
00916 
00917   source->list_cmd = g_strdup (list_cmd);
00918   source->get_cmd = g_strdup (get_cmd);
00919 
00920   refresh_command (source);
00921 
00922   /* Sources added later get sacnned earlier */
00923   clib_sources = g_list_prepend (clib_sources, source);
00924 
00925   return source;
00926 }
00927 
00942 const CLibSource *s_clib_add_scm (SCM listfunc, SCM getfunc, const gchar *name)
00943 {
00944   CLibSource *source;
00945   gchar *realname;
00946 
00947   if (name == NULL) {
00948     s_log_message (_("Cannot add library: name not specified\n"));
00949     return NULL;
00950   }  
00951   
00952   realname = uniquify_source_name (name);
00953 
00954   if (scm_is_false (scm_procedure_p (listfunc)) 
00955       && scm_is_false (scm_procedure_p (getfunc))) {
00956     s_log_message (_("Cannot add Scheme-library [%s]: callbacks must be closures\n"),
00957            realname);
00958     return NULL;
00959   }
00960 
00961   source = g_new0 (CLibSource, 1);
00962   source->type = CLIB_SCM;
00963   source->name = realname;
00964   source->list_fn = scm_gc_protect_object (listfunc);
00965   source->get_fn = scm_gc_protect_object (getfunc);
00966 
00967   refresh_scm (source);
00968 
00969   clib_sources = g_list_prepend (clib_sources, source);
00970 
00971   return source;
00972 }
00973 
00981 const gchar *s_clib_source_get_name (const CLibSource *source)
00982 {
00983   if (source == NULL) return NULL;
00984   return source->name;
00985 }
00986 
00998 GList *s_clib_source_get_symbols (const CLibSource *source)
00999 {
01000   if (source == NULL) return NULL;
01001   return g_list_copy(source->symbols);
01002 }
01003 
01004 
01013 const gchar *s_clib_symbol_get_name (const CLibSymbol *symbol)
01014 {
01015   if (symbol == NULL) return NULL;
01016   return symbol->name;
01017 }
01018 
01036 gchar *s_clib_symbol_get_filename (const CLibSymbol *symbol)
01037 {
01038   if (symbol == NULL) return NULL;
01039 
01040   if (symbol->source->type != CLIB_DIR) return NULL;
01041 
01042   return g_build_filename(symbol->source->directory, symbol->name, NULL);
01043 }
01044 
01052 const CLibSource *s_clib_symbol_get_source (const CLibSymbol *symbol)
01053 {
01054   if (symbol == NULL) return NULL;
01055   return symbol->source;
01056 }
01057 
01068 static gchar *get_data_directory (const CLibSymbol *symbol)
01069 {
01070   gchar *filename = NULL;
01071   gchar *data = NULL;
01072   GError *e = NULL;
01073 
01074   g_return_val_if_fail ((symbol != NULL), NULL);
01075   g_return_val_if_fail ((symbol->source->type == CLIB_DIR), NULL);
01076 
01077   filename = g_build_filename(symbol->source->directory, 
01078                   symbol->name, NULL);
01079 
01080   g_file_get_contents (filename, &data, NULL, &e);
01081 
01082   if (e != NULL) {
01083     s_log_message (_("Failed to load symbol from file [%s]: %s\n"),
01084            filename, e->message);
01085     g_error_free (e);
01086   }
01087 
01088   g_free (filename);
01089   return data;
01090 }
01091 
01102 static gchar *get_data_command (const CLibSymbol *symbol)
01103 {
01104   gchar *command;
01105   gchar *result;
01106 
01107   g_return_val_if_fail ((symbol != NULL), NULL);
01108   g_return_val_if_fail ((symbol->source->type == CLIB_CMD), NULL);
01109   
01110   command = g_strdup_printf ("%s %s", symbol->source->get_cmd, 
01111                           symbol->name);
01112 
01113   result = run_source_command ( command );
01114 
01115   g_free (command);
01116 
01117   return result;
01118 }
01119 
01130 static gchar *get_data_scm (const CLibSymbol *symbol)
01131 {
01132   SCM symdata;
01133   char *tmp;
01134   gchar *result;
01135 
01136   g_return_val_if_fail ((symbol != NULL), NULL);
01137   g_return_val_if_fail ((symbol->source->type == CLIB_SCM), NULL);
01138 
01139   symdata = scm_call_1 (symbol->source->get_fn, 
01140             scm_from_utf8_string (symbol->name));
01141 
01142   if (!scm_is_string (symdata)) {
01143     s_log_message (_("Failed to load symbol data [%s] from source [%s]\n"),
01144            symbol->name, symbol->source->name);
01145     return NULL;
01146   }
01147 
01148   /* Need to make sure that the correct free() function is called
01149    * on strings allocated by Guile. */
01150   tmp = scm_to_utf8_string (symdata);
01151   result = g_strdup(tmp);
01152   free (tmp);
01153 
01154   return result;
01155 }
01156 
01168 gchar *s_clib_symbol_get_data (const CLibSymbol *symbol)
01169 {
01170   CacheEntry *cached;
01171   gchar *data;
01172   gpointer symptr;
01173   gint n;
01174 
01175   g_return_val_if_fail ((symbol != NULL), NULL);
01176   g_return_val_if_fail ((symbol->source != NULL), NULL);
01177 
01178   /* Trickery to bypass effects of const */
01179   symptr = (gpointer) symbol;
01180 
01181   /* First, try the cache. */
01182   cached = g_hash_table_lookup (clib_symbol_cache, symptr);
01183   if (cached != NULL) {
01184     cached->accessed = time(NULL);
01185     return g_strdup(cached->data);
01186   }
01187 
01188   /* If the symbol wasn't found in the cache, get it directly. */
01189   switch (symbol->source->type)
01190     {
01191     case CLIB_DIR:
01192       data = get_data_directory (symbol);
01193       break;
01194     case CLIB_CMD:
01195       data = get_data_command (symbol);
01196       break;
01197     case CLIB_SCM:
01198       data = get_data_scm (symbol);
01199       break;
01200     default:
01201       g_critical("s_clib_symbol_get_data: source %p has bad source type %i\n",
01202                  symbol->source, (gint) symbol->source->type);
01203       return NULL;
01204     }
01205 
01206   if (data == NULL) return NULL;
01207 
01208   /* Cache the symbol data */
01209   cached = g_new (CacheEntry, 1);
01210   cached->ptr = (CLibSymbol *) symptr;
01211   cached->data = g_strdup (data);
01212   cached->accessed = time (NULL);
01213   g_hash_table_insert (clib_symbol_cache, symptr, cached);
01214 
01215   /* Clean out the cache if it's too full */
01216   n = g_hash_table_size (clib_symbol_cache);
01217   if (n > CLIB_MAX_SYMBOL_CACHE) {
01218     for ( ; n > CLIB_MIN_SYMBOL_CACHE; n--) {
01219       g_hash_table_foreach (clib_symbol_cache,
01220                             (GHFunc) cache_find_oldest,
01221                             &cached);
01222       g_hash_table_remove (clib_symbol_cache, cached->ptr);
01223     }
01224   }
01225 
01226   return data;
01227 }
01228 
01251 GList *s_clib_search (const gchar *pattern, const CLibSearchMode mode)
01252 {  
01253   GList *sourcelist;
01254   GList *symlist;
01255   GList *result = NULL;
01256   CLibSource *source;
01257   CLibSymbol *symbol;
01258   GPatternSpec *globpattern = NULL;
01259   gchar *key;
01260   gchar keytype;
01261 
01262   if (pattern == NULL) return NULL;
01263 
01264   /* Use different cache keys depending on what sort of search is being done */
01265   switch (mode)
01266     {
01267     case CLIB_GLOB:
01268       keytype = 'g';
01269       break;
01270     case CLIB_EXACT:
01271       keytype = 's';
01272       break;
01273     default:
01274       g_critical ("s_clib_search: Bad search mode %i\n", mode);
01275       return NULL;
01276     }
01277   key = g_strdup_printf("%c%s", keytype, pattern);
01278 
01279   /* Check to see if the query is already in the cache */
01280   result = (GList *) g_hash_table_lookup (clib_search_cache, key);
01281   if (result != NULL) {
01282     g_free (key);
01283     return g_list_copy (result);
01284   }
01285 
01286   if (mode == CLIB_GLOB) {
01287     globpattern = g_pattern_spec_new(pattern);
01288   }
01289 
01290   for (sourcelist = clib_sources; 
01291        sourcelist != NULL; 
01292        sourcelist = g_list_next(sourcelist)) {
01293 
01294     source = (CLibSource *) sourcelist->data;
01295 
01296     for (symlist = source->symbols;
01297      symlist != NULL;
01298      symlist = g_list_next(symlist)) {
01299     
01300       symbol = (CLibSymbol *) symlist->data;
01301 
01302       switch (mode)
01303     {
01304     case CLIB_EXACT:
01305       if (strcmp (pattern, symbol->name) == 0) {
01306         result = g_list_prepend (result, symbol);
01307       }
01308       break;
01309     case CLIB_GLOB:
01310       if (g_pattern_match_string (globpattern, symbol->name)) {
01311         result = g_list_prepend (result, symbol);
01312       }
01313       break;
01314     }
01315     }
01316   }
01317 
01318   result = g_list_reverse (result);
01319 
01320   if (globpattern != NULL) {
01321     g_pattern_spec_free (globpattern);
01322   }
01323 
01324   g_hash_table_insert (clib_search_cache, key, g_list_copy (result));
01325   /* __don't__ free key here, it's stored by the hash table! */
01326 
01327   return result;
01328 }
01329 
01330 
01331 
01332 
01339 void s_clib_flush_search_cache ()
01340 {
01341   g_hash_table_remove_all (clib_search_cache);  /* Introduced in glib 2.12 */
01342 }
01343 
01344 
01351 void s_clib_flush_symbol_cache ()
01352 {
01353   g_hash_table_remove_all (clib_symbol_cache);  /* Introduced in glib 2.12 */
01354 }
01355 
01362 void
01363 s_clib_symbol_invalidate_data (const CLibSymbol *symbol)
01364 {
01365   g_hash_table_remove (clib_symbol_cache, (gpointer) symbol);
01366 }
01367 
01377 const CLibSymbol *s_clib_get_symbol_by_name (const gchar *name)
01378 {
01379   GList *symlist = NULL;
01380   const CLibSymbol *retval;
01381 
01382   symlist = s_clib_search (name, CLIB_EXACT);
01383 
01384   if (symlist == NULL) {
01385     s_log_message (_("Component [%s] was not found in the component library\n"),
01386                    name);
01387     return NULL;
01388   }
01389 
01390   if (g_list_next (symlist) != NULL) { /* More than one symbol */
01391     s_log_message (_("More than one component found with name [%s]\n"),
01392                    name);
01393   }
01394 
01395   retval = (CLibSymbol *) symlist->data;
01396   g_list_free (symlist);
01397 
01398   return retval;
01399 }
01400 
01412 gchar *s_clib_symbol_get_data_by_name (const gchar *name)
01413 {
01414   const CLibSymbol *symbol;
01415 
01416   symbol = s_clib_get_symbol_by_name (name);
01417   if (symbol == NULL) return NULL;
01418   return s_clib_symbol_get_data (symbol);
01419 }
01420 
01440 GList *s_toplevel_get_symbols (const TOPLEVEL *toplevel)
01441 {
01442   GList *result = NULL;
01443   GList *iter = NULL;
01444   OBJECT *o = NULL;
01445   PAGE *page;
01446   GList *symlist = NULL;
01447   CLibSymbol *sym = NULL;
01448   const GList *p_iter;
01449   const GList *o_iter;
01450 
01451   g_return_val_if_fail ((toplevel != NULL), NULL);
01452 
01453   for ( p_iter = geda_list_get_glist( toplevel->pages );
01454         p_iter != NULL;
01455         p_iter = g_list_next( p_iter )) {
01456     page = (PAGE *)p_iter->data;
01457     for (o_iter = s_page_objects (page);
01458          o_iter != NULL;
01459          o_iter = g_list_next (o_iter)) {
01460       o = (OBJECT *)o_iter->data;
01461       if (o->type != OBJ_COMPLEX) continue;
01462       if (o->complex_basename == NULL)  continue;
01463       
01464       /* Since we're not looking at embedded symbols, the first
01465        * component with the given name will be the one we need.
01466        * N.b. we don't use s_clib_get_symbol_by_name() because it's
01467        * spammeh. */
01468       symlist = s_clib_search (o->complex_basename, CLIB_EXACT);
01469       if (symlist == NULL) continue;
01470       sym = (CLibSymbol *) symlist->data;
01471       g_list_free (symlist);
01472       
01473       /* We do the list insertion by evilly comparing pointers.  This
01474        * is okay, because we always take the first symbol with the
01475        * given name, and symbol pointers don't change while this
01476        * function is running (we hope).  Note that this creates a
01477        * sorted list.*/
01478       for (iter = result;
01479            iter != NULL;
01480            iter = g_list_next(iter)) {
01481         if (iter->data == sym) {
01482           break; /* Already in list */
01483         }
01484         if (compare_symbol_name (iter->data, sym) > 0) {
01485           /* not in list yet, and gone past point where it should go */
01486           result = g_list_insert_before (result, iter, sym);
01487           break;
01488         }
01489       }
01490       if (iter == NULL) {
01491         /* not in list yet, and at end of list */
01492         result = g_list_append (result, sym);    
01493       }
01494     }
01495   }
01496 
01497   return result;
01498 }
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Defines