libgeda
|
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 }