/*! \brief Initialise the component library. * \par Function Description * Resets and initialises the component library. * * \warning This function must be called before any other functions * from s_clib.c. */ void s_clib_init () { if (clib_sources != NULL) { s_clib_free (); } if (clib_search_cache != NULL) { s_clib_flush_search_cache(); } else { clib_search_cache = g_hash_table_new_full ((GHashFunc) g_str_hash, (GEqualFunc)g_str_equal, (GDestroyNotify) g_free, (GDestroyNotify) g_list_free); } if (clib_symbol_cache != NULL) { s_clib_flush_symbol_cache(); } else { clib_symbol_cache = g_hash_table_new_full ((GHashFunc) g_direct_hash, (GEqualFunc) g_direct_equal, NULL, (GDestroyNotify) free_symbol_cache_entry); } }
/*! \brief Re-poll a library command for symbols. * \par Function Description * Runs a library command, requesting a list of available symbols, * and updates the source with the new list. * * Private function used only in s_clib.c. */ static void refresh_command (CLibSource *source) { gchar *cmdout; TextBuffer *tb; const gchar *line; CLibSymbol *symbol; gchar *name; g_return_if_fail (source != NULL); g_return_if_fail (source->type == CLIB_CMD); /* Clear the current symbol list */ g_list_foreach (source->symbols, (GFunc) free_symbol, NULL); g_list_free (source->symbols); source->symbols = NULL; /* Run the command to get the list of symbols */ cmdout = run_source_command (source->list_cmd); if (cmdout == NULL) return; /* Use a TextBuffer to help reading out the lines of the output */ tb = s_textbuffer_new (cmdout, -1, "s_clib.c::refresh_command()"); while (1) { line = s_textbuffer_next_line (tb); if (line == NULL) break; if (line[0] == '.') continue; /* TODO is this sane? */ name = geda_string_get_first_line (g_strdup(line)); /* skip symbols already known about */ if (source_has_symbol (source, name) != NULL) { g_free (name); continue; } symbol = g_new0 (CLibSymbol, 1); symbol->source = source; symbol->name = name; /* Prepend because it's faster and it doesn't matter what order we * add them. */ source->symbols = g_list_prepend (source->symbols, symbol); } s_textbuffer_free (tb); g_free (cmdout); /* Sort all symbols by name. */ source->symbols = g_list_sort (source->symbols, (GCompareFunc) compare_symbol_name); s_clib_flush_search_cache(); s_clib_flush_symbol_cache(); }
/*! \brief Re-poll a scheme procedure for symbols. * \par Function Description * Calls a Scheme procedure to obtain a list of available symbols, * and updates the source with the new list * * Private function used only in s_clib.c. */ static void refresh_scm (CLibSource *source) { SCM symlist; SCM symname; CLibSymbol *symbol; char *tmp; g_return_if_fail (source != NULL); g_return_if_fail (source->type == CLIB_SCM); /* Clear the current symbol list */ g_list_foreach (source->symbols, (GFunc) free_symbol, NULL); g_list_free (source->symbols); source->symbols = NULL; symlist = scm_call_0 (source->list_fn); if (scm_is_false (scm_list_p (symlist))) { s_log_message (_("Failed to scan library [%1$s]: Scheme function returned non-list."), source->name); return; } while (!scm_is_null (symlist)) { symname = SCM_CAR (symlist); if (!scm_is_string (symname)) { s_log_message (_("Non-string symbol name while scanning library [%1$s]"), source->name); } else { symbol = g_new0 (CLibSymbol, 1); symbol->source = source; /* Need to make sure that the correct free() function is called * on strings allocated by Guile. */ tmp = scm_to_utf8_string (symname); symbol->name = g_strdup(tmp); free (tmp); /* Prepend because it's faster and it doesn't matter what order we * add them. */ source->symbols = g_list_prepend (source->symbols, symbol); } symlist = SCM_CDR (symlist); } /* Now sort the list of symbols by name. */ source->symbols = g_list_sort (source->symbols, (GCompareFunc) compare_symbol_name); s_clib_flush_search_cache(); s_clib_flush_symbol_cache(); }
/*! \brief Rescan a directory for symbols. * \par Function Description * Rescans a directory for symbols. * * \todo Does this need to do something more sane with subdirectories * than just skipping them silently? * * Private function used only in s_clib.c. */ static void refresh_directory (CLibSource *source) { CLibSymbol *symbol; GDir *dir; const gchar *entry; gchar *low_entry; gchar *fullpath; gboolean isfile; GError *e = NULL; g_return_if_fail (source != NULL); g_return_if_fail (source->type == CLIB_DIR); /* Clear the current symbol list */ g_list_foreach (source->symbols, (GFunc) free_symbol, NULL); g_list_free (source->symbols); source->symbols = NULL; /* Open the directory for reading. */ dir = g_dir_open (source->directory, 0, &e); if (e != NULL) { s_log_message (_("Failed to open directory [%1$s]: %2$s"), source->directory, e->message); g_error_free (e); return; } while ((entry = g_dir_read_name (dir)) != NULL) { /* skip ".", ".." & hidden files */ if (entry[0] == '.') continue; /* skip subdirectories (for now) */ fullpath = g_build_filename (source->directory, entry, NULL); isfile = g_file_test (fullpath, G_FILE_TEST_IS_REGULAR); g_free (fullpath); if (!isfile) continue; /* skip filenames that we already know about. */ if (source_has_symbol (source, entry) != NULL) continue; /* skip filenames which don't have the right suffix. */ low_entry = g_utf8_strdown (entry, -1); if (!g_str_has_suffix (low_entry, SYM_FILENAME_FILTER)) { g_free (low_entry); continue; } g_free (low_entry); /* Create and add new symbol record */ symbol = g_new0 (CLibSymbol, 1); symbol->source = source; symbol->name = g_strdup(entry); /* Prepend because it's faster and it doesn't matter what order we * add them. */ source->symbols = g_list_prepend (source->symbols, symbol); } entry = NULL; g_dir_close (dir); /* Now sort the list of symbols by name. */ source->symbols = g_list_sort (source->symbols, (GCompareFunc) compare_symbol_name); s_clib_flush_search_cache(); s_clib_flush_symbol_cache(); }
/*! \brief Update a component. * * \par Function Description * Updates \a o_current to the latest version of the symbol available * in the symbol library, while preserving any attributes set in the * current schematic. On success, returns the new OBJECT which * replaces \a o_current on the page; \a o_current is deleted. On * failure, returns NULL, and \a o_current is left unchanged. * * \param [in] w_current The GSCHEM_TOPLEVEL object. * \param [in,out] o_current The OBJECT to be updated. * * \return the new OBJECT that replaces \a o_current. */ OBJECT * o_update_component (GSCHEM_TOPLEVEL *w_current, OBJECT *o_current) { TOPLEVEL *toplevel = w_current->toplevel; OBJECT *o_new; PAGE *page; GList *new_attribs; GList *old_attribs; GList *iter; const CLibSymbol *clib; g_return_val_if_fail (o_current != NULL, NULL); g_return_val_if_fail (o_current->type == OBJ_COMPLEX, NULL); g_return_val_if_fail (o_current->complex_basename != NULL, NULL); page = o_get_page (toplevel, o_current); /* This should be replaced with API to invalidate only the specific * symbol name we want to update */ s_clib_flush_symbol_cache (); clib = s_clib_get_symbol_by_name (o_current->complex_basename); if (clib == NULL) { s_log_message (_("Could not find symbol [%s] in library. Update failed.\n"), o_current->complex_basename); return NULL; } /* Unselect the old object. */ o_selection_remove (toplevel, page->selection_list, o_current); /* Create new object and set embedded */ o_new = o_complex_new (toplevel, OBJ_COMPLEX, DEFAULT_COLOR, o_current->complex->x, o_current->complex->y, o_current->complex->angle, o_current->complex->mirror, clib, o_current->complex_basename, 1); if (o_complex_is_embedded (o_current)) { o_embed (toplevel, o_new); } new_attribs = o_complex_promote_attribs (toplevel, o_new); /* Cull any attributes from new COMPLEX that are already attached to * old COMPLEX. Note that the new_attribs list is kept consistent by * setting GList data pointers to NULL if their OBJECTs are * culled. At the end, the new_attribs list is updated by removing * all list items with NULL data. This is slightly magic, but * works. */ for (iter = new_attribs; iter != NULL; iter = g_list_next (iter)) { OBJECT *attr_new = iter->data; gchar *name; gchar *value; g_assert (attr_new->type == OBJ_TEXT); o_attrib_get_name_value (attr_new, &name, NULL); value = o_attrib_search_attached_attribs_by_name (o_current, name, 0); if (value != NULL) { o_attrib_remove (toplevel, &o_new->attribs, attr_new); s_delete_object (toplevel, attr_new); iter->data = NULL; } g_free (name); g_free (value); } new_attribs = g_list_remove_all (new_attribs, NULL); /* Detach attributes from old OBJECT and attach to new OBJECT */ old_attribs = g_list_copy (o_current->attribs); o_attrib_detach_all (toplevel, o_current); o_attrib_attach_list (toplevel, old_attribs, o_new, 1); g_list_free (old_attribs); /* Add new attributes to page */ s_page_append_list (toplevel, page, new_attribs); /* Update pinnumbers for current slot */ s_slot_update_object (toplevel, o_new); /* Replace old OBJECT with new OBJECT */ s_page_replace (toplevel, page, o_current, o_new); s_delete_object (toplevel, o_current); /* Select new OBJECT */ o_selection_add (toplevel, page->selection_list, o_new); /* mark the page as modified */ toplevel->page_current->CHANGED = 1; o_undo_savestate (w_current, UNDO_ALL); return o_new; }