[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: gEDA-dev: [PATCH][RFC]New component library system



Hi folks,

I have resolved a couple of issues with my patches:

(1) They now compile (which helps) -- thanks to Ivan and Igor

(2) Component sources and symbols are now sorted by name.

I attach a revised patch.  Note that it's a straightforward diff against CVS 
this time; those who wish to look at the detailed diffs should visit:

  http://repo.or.cz/w/geda-gaf/peter-b.git?a=shortlog;h=libraries

Or clone the git repository[1] using:

  cg-clone git://repo.or.cz/geda-gaf/peter-b.git#libraries

At this point, the revised code behaves *exactly* the same as the old code 
from a user's point of view, so I'd like to get it into CVS ASAP after Ales 
release the next version.  Then I'll be able then gradually work through the 
other items on the todo list (optimisation etc) without having to diverge too 
far from CVS.  In particular, I'd like to get it into CVS *before* the next 
code sprint, so that the deluge of patches bound to happen during the sprint 
doesn't mess me up too badly.

Cheers,

Peter

[1] Note that the "master" branch in this repository is just CVS HEAD, and as 
such isn't particular informative.

-- 
Fisher Society                              http://tinyurl.com/o39w2
CU Small-Bore Club                          http://tinyurl.com/mwrc9

      09f911029d74e35bd84156c5635688c0            peter-b.co.uk
diff --git a/gschem/src/g_hook.c b/gschem/src/g_hook.c
index 1041185..7f8b83a 100644
--- a/gschem/src/g_hook.c
+++ b/gschem/src/g_hook.c
@@ -669,9 +669,8 @@ SCM g_add_component(SCM page_smob, SCM scm_comp_name, SCM scm_x, SCM scm_y,
   TOPLEVEL *w_current;
   PAGE *page;
   gboolean selectable, mirror;
-  gchar *comp_name, *clib;
+  gchar *comp_name;
   int x, y, angle;
-  GSList *clibs = NULL;
   OBJECT *new_object;
 
   /* Return if scm_comp_name is NULL (an empty list) or scheme's FALSE */
@@ -712,31 +711,16 @@ SCM g_add_component(SCM page_smob, SCM scm_comp_name, SCM scm_x, SCM scm_y,
     return SCM_BOOL_F;
   }
 
-  clibs = (GSList *) s_clib_search_basename (comp_name);
-  if (clibs == NULL) {
-    /* Component not found */
-    s_log_message ("add-component-at-xy: Component with name [%s] not found.\n",
-		   comp_name);
-    return SCM_BOOL_F;    
-  } 
-
-  g_assert(clibs != NULL);
-  if (g_slist_next (clibs)) {
-    s_log_message ("add-component-at-xy: More than one component found with name [%s]\n",
-		   comp_name);
-    /* PB: for now, use the first directory in clibs */
-    /* PB: maybe open a dialog to select the right one? */
-  }
-  clib = (gchar*)clibs->data;
-  
-  new_object = page->object_tail = o_complex_add(w_current, 
-						 page->object_tail, NULL, 
-						 'C', 
-						 WHITE, 
-						 x, y, 
-						 angle, mirror,
-						 clib, comp_name, 
-						 selectable, FALSE);
+  new_object 
+    = page->object_tail 
+    = o_complex_add_by_name(w_current, 
+			    page->object_tail, NULL, 
+			    'C', 
+			    WHITE, 
+			    x, y, 
+			    angle, mirror,
+			    comp_name, 
+			    selectable, FALSE);
   
   /* 
    * For now, do not redraw the newly added complex, since this might cause
diff --git a/gschem/src/i_callbacks.c b/gschem/src/i_callbacks.c
index dc69781..cfe3518 100644
--- a/gschem/src/i_callbacks.c
+++ b/gschem/src/i_callbacks.c
@@ -2848,13 +2848,14 @@ DEFINE_I_CALLBACK(hierarchy_down_schematic)
 /*! \todo Finish function documentation!!!
  *  \brief
  *  \par Function Description
- *
+ *  \todo Allow hierarchy descent into embedded components (using a
+ *  component library lookup)
  */
 DEFINE_I_CALLBACK(hierarchy_down_symbol)
 {
   TOPLEVEL *w_current = (TOPLEVEL *) data;
   OBJECT *object;
-  char *filename;
+  const CLibSymbol *sym;
 
   exit_if_null(w_current);
 
@@ -2863,12 +2864,11 @@ DEFINE_I_CALLBACK(hierarchy_down_symbol)
     /* only allow going into symbols */
     if (object->type == OBJ_COMPLEX &&
         !o_complex_is_embedded (object)) {
-      filename = g_strconcat (object->complex_clib,
-                              G_DIR_SEPARATOR_S,
-                              object->complex_basename, NULL);
-      s_log_message(_("Searching for symbol [%s]\n"), filename);
-      s_hierarchy_down_symbol(w_current, filename, w_current->page_current);
-      g_free(filename);
+      sym = object->complex_clib;
+      s_log_message(_("Searching for symbol [%s]\n"), 
+		    s_clib_symbol_get_name(sym));
+      s_hierarchy_down_symbol(w_current, sym, 
+			      w_current->page_current);
       /* s_hierarchy_down_symbol() will not zoom the loaded page */
       a_zoom_extents(w_current,
                      w_current->page_current->object_head,
@@ -2909,6 +2909,7 @@ DEFINE_I_CALLBACK(hierarchy_documentation)
   char *attrib_device = NULL;
   char *attrib_value = NULL;
   OBJECT *object = NULL;
+  const gchar *sourcename = NULL;
 
   exit_if_null(w_current);
 
@@ -2932,11 +2933,13 @@ DEFINE_I_CALLBACK(hierarchy_documentation)
       if (!attrib_value) {
         attrib_value = o_attrib_search_name(object->complex->prim_objs, "value", 0);
       }
+      
+      sourcename = s_clib_source_get_name (s_clib_symbol_get_source(object->complex_clib));
       initiate_gschemdoc(attrib_doc,
                          attrib_device,
                          attrib_value,
                          object->complex_basename,
-                         object->complex_clib);
+                         sourcename);
 
       if (attrib_doc) g_free(attrib_doc);
       if (attrib_device) g_free(attrib_device);
diff --git a/gschem/src/o_complex.c b/gschem/src/o_complex.c
index 6c014ba..edb52a1 100644
--- a/gschem/src/o_complex.c
+++ b/gschem/src/o_complex.c
@@ -147,7 +147,8 @@ void o_complex_start(TOPLEVEL *w_current, int screen_x, int screen_y)
 		&(w_current->page_current->complex_place_list),
 		OBJ_COMPLEX, WHITE, x, y, 0, 0,
 		w_current->internal_clib,
-		w_current->internal_basename, 1, TRUE);
+		s_clib_symbol_get_name (w_current->internal_clib), 
+		1, TRUE);
   w_current->ADDING_SEL = 0;
   w_current->DONT_DRAW_CONN = 0;
 
@@ -260,7 +261,7 @@ void o_complex_end(TOPLEVEL *w_current, int screen_x, int screen_y)
   OBJECT *o_current;
   OBJECT *o_start;
   OBJECT *o_temp;
-  char *include_filename;
+  char *buffer;
   int temp, new_angle, i;
   GList *connected_objects=NULL;
 
@@ -277,20 +278,18 @@ void o_complex_end(TOPLEVEL *w_current, int screen_x, int screen_y)
 #endif
 
   if (w_current->include_complex) {
-    include_filename = g_strconcat (w_current->internal_clib,
-                                    G_DIR_SEPARATOR_S,
-                                    w_current->internal_basename,
-                                    NULL);
+    buffer = s_clib_symbol_get_data (w_current->internal_clib);
 
     w_current->ADDING_SEL=1;
     o_start = w_current->page_current->object_tail;
     w_current->page_current->object_tail =
-      o_read(w_current,
-             w_current->page_current->object_tail,
-             include_filename);
+      o_read_buffer(w_current,
+		    w_current->page_current->object_tail,
+		    buffer, -1,
+		    s_clib_symbol_get_name(w_current->internal_clib));
     o_start = o_start->next;
     w_current->ADDING_SEL=0;
-
+    
     o_complex_world_translate(w_current, x, y, o_start);
 
     o_temp = o_start;
@@ -308,7 +307,7 @@ void o_complex_end(TOPLEVEL *w_current, int screen_x, int screen_y)
     o_cue_draw_list(w_current, connected_objects);
     g_list_free(connected_objects);
 
-    g_free(include_filename);
+    g_free(buffer);
 
     if (w_current->actionfeedback_mode == OUTLINE) {
 #if 0
@@ -358,7 +357,8 @@ void o_complex_end(TOPLEVEL *w_current, int screen_x, int screen_y)
                   w_current->page_current->object_tail, NULL,
                   OBJ_COMPLEX, WHITE, x, y, w_current->complex_rotate, 0,
                   w_current->internal_clib,
-                  w_current->internal_basename, 1, TRUE);
+                  s_clib_symbol_get_name (w_current->internal_clib), 
+		  1, TRUE);
 
   /* complex rotate post processing */
   o_temp = o_temp->next; /* skip over last object */
@@ -398,21 +398,7 @@ void o_complex_end(TOPLEVEL *w_current, int screen_x, int screen_y)
 
   /* put code here to deal with emebedded stuff */
   if (w_current->embed_complex) {
-    char* new_basename;
-
-    g_free(o_current->complex_clib);
-
-    o_current->complex_clib = g_strdup ("EMBEDDED");
-
-    new_basename = g_strconcat ("EMBEDDED",
-                                o_current->complex_basename,
-                                NULL);
-
-    g_free(o_current->complex_basename);
-
-    o_current->complex_basename = g_strdup (new_basename);
-
-    g_free(new_basename);
+    o_current->complex_embedded = TRUE;
   }
 
   /* check for nulls in all this hack */
diff --git a/gschem/src/o_copy.c b/gschem/src/o_copy.c
index 5050208..091ada2 100644
--- a/gschem/src/o_copy.c
+++ b/gschem/src/o_copy.c
@@ -222,8 +222,7 @@ void o_copy_end(TOPLEVEL *w_current)
       case(OBJ_COMPLEX):
       case(OBJ_PLACEHOLDER):
         w_current->ADDING_SEL=1; 
-        if (object->complex_clib && 
-            strncmp(object->complex_clib, "EMBEDDED", 8) == 0) {
+        if (o_complex_is_embedded(object)) {
 
           new_object = (OBJECT *) 
             o_complex_copy_embedded(w_current, 
@@ -232,7 +231,7 @@ void o_copy_end(TOPLEVEL *w_current)
 
         } else {
           new_object = (OBJECT *) o_complex_copy(w_current,
-                                                 return_tail(new_objects_head), 
+                                                 return_tail(new_objects_head),
                                                  object);
         }
         w_current->ADDING_SEL=0; 
diff --git a/gschem/src/o_misc.c b/gschem/src/o_misc.c
index cbc9a9c..f870629 100644
--- a/gschem/src/o_misc.c
+++ b/gschem/src/o_misc.c
@@ -1057,38 +1057,37 @@ void o_update_component(TOPLEVEL *w_current, OBJECT *o_current)
   OBJECT *tmp_list, *new_complex;
   ATTRIB *new_attribs, *a_current;
   gboolean is_embedded;
-  gchar *basename, *clib;
+  const CLibSymbol *clib;
 
   g_return_if_fail (o_current != NULL);
 
   is_embedded = o_complex_is_embedded (o_current);
 
-  /* identify symbol name */
-  if (is_embedded) {
-    const GSList *clibs;
+  /* find symbol if not known */
+  if (o_current->complex_clib == NULL) {
+    const GList *symlist = NULL;
     
-    /* skip over EMBEDDED word */
-    basename = g_strdup (o_current->complex_basename + 8);
-
-    /* search for basename in component library */
-    clibs    = s_clib_search_basename (basename);
-    if (!clibs) {
-      s_log_message (_("Could not unembedded component, could not find appropriate .sym file\n"));
-      s_log_message (_("Component still embedded and not updated\n"));
+    g_assert (o_current->complex_basename != NULL);
+    symlist = s_clib_glob (o_current->complex_basename);
+
+    if (symlist == NULL) {
+      s_log_message (_("Could not find symbol [%s] in library. Update failed.\n"), 
+		     o_current->complex_basename);
       return;
-    }
-    if (g_slist_next (clibs)) {
-      s_log_message (_("More than one component found with name [%s]\n"),
-                     basename);
+    } else {
+      if (g_list_next (symlist) != NULL) {
+	s_log_message (_("More than one component found with name [%s]\n"),
+		       o_current->complex_basename);
       /* PB: for now, use the first directory in clibs */
       /* PB: maybe open a dialog to select the right one? */
+      }
     }
-    clib = (gchar*)clibs->data;
-  } else {
-    basename = o_current->complex_basename;
-    clib     = o_current->complex_clib;
+
+    o_current->complex_clib = (CLibSymbol *) symlist->data;
   }
 
+  clib = o_current->complex_clib;
+
   /* erase the complex object */
   o_erase_single (w_current, o_current);
   /* delete its connections */
@@ -1107,7 +1106,8 @@ void o_update_component(TOPLEVEL *w_current, OBJECT *o_current)
                                o_current->complex->y,
                                o_current->complex->angle,
                                o_current->complex->mirror,
-                               clib, basename, 1, TRUE);
+                               clib, o_current->complex_basename, 
+			       1, TRUE);
 
   /* updating the old complex with data from the new one */
   /* first process the prim_objs: */
@@ -1175,11 +1175,8 @@ void o_update_component(TOPLEVEL *w_current, OBJECT *o_current)
 		   o_current);
   o_redraw_single (w_current, o_current);
 
-  if (is_embedded) {
-    /* we previously allocated memory for basename */
-    g_free (basename);
-    /* clib should not be freed here since it is owned by libgeda */
-  }
+  /* Re-flag as embedded if necessary */
+  o_current->complex_embedded = is_embedded;
     
   /* mark the page as modified */
   w_current->page_current->CHANGED = 1;
diff --git a/gschem/src/o_net.c b/gschem/src/o_net.c
index 23c1370..7db67b2 100644
--- a/gschem/src/o_net.c
+++ b/gschem/src/o_net.c
@@ -851,7 +851,7 @@ int o_net_add_busrippers(TOPLEVEL *w_current, OBJECT *net_obj,
   int made_changes = FALSE;
   const int ripper_size = w_current->bus_ripper_size;
   int complex_angle = 0;
-  char *clib = NULL;
+  const CLibSymbol *rippersym = NULL;
   
   length = o_line_length(net_obj);
 
@@ -1123,10 +1123,11 @@ int o_net_add_busrippers(TOPLEVEL *w_current, OBJECT *net_obj,
     s_conn_remove(w_current, net_obj);
 
     if (w_current->bus_ripper_type == COMP_BUS_RIPPER) {
-      const GSList *clibs = s_clib_search_basename (w_current->bus_ripper_symname);
-      if (clibs != NULL) {
-        clib = (gchar*)clibs->data;
+      GList *symlist = s_clib_glob (w_current->bus_ripper_symname);
+      if (symlist != NULL) {
+        rippersym = (CLibSymbol *) symlist->data;
       }
+      g_list_free (symlist);
     }
     
     for (i = 0; i < ripper_count; i++) {
@@ -1138,7 +1139,7 @@ int o_net_add_busrippers(TOPLEVEL *w_current, OBJECT *net_obj,
                     rippers[i].x[1], rippers[i].y[1]);
       } else {
 
-        if (clib) {
+        if (rippersym != NULL) {
           w_current->page_current->object_tail = 
           (OBJECT *) o_complex_add(
                                    w_current,
@@ -1147,12 +1148,12 @@ int o_net_add_busrippers(TOPLEVEL *w_current, OBJECT *net_obj,
                                    OBJ_COMPLEX, WHITE,
                                    rippers[i].x[0], rippers[i].y[0],
                                    complex_angle, 0,
-                                   clib,
+                                   rippersym,
                                    w_current->bus_ripper_symname, 1, TRUE);
           
           o_complex_draw(w_current,w_current->page_current->object_tail);
         } else {
-          s_log_message(_("Could not find %s in any component-library\n"),
+          s_log_message(_("Bus ripper symbol [%s] was not found in any component library\n"),
                         w_current->bus_ripper_symname);
         }
       }
diff --git a/gschem/src/x_compselect.c b/gschem/src/x_compselect.c
index 5f6e1b8..6997169 100644
--- a/gschem/src/x_compselect.c
+++ b/gschem/src/x_compselect.c
@@ -86,11 +86,11 @@ x_compselect_callback_response (GtkDialog *dialog,
 
   switch (arg1) {
       case GTK_RESPONSE_APPLY: {
-        gchar *filename, *directory, *component;
+	CLibSymbol *symbol;
         CompselectBehavior behavior;
         
         g_object_get (compselect,
-                      "filename", &filename,
+                      "symbol", &symbol,
                       "behavior", &behavior,
                       NULL);
 
@@ -108,26 +108,12 @@ x_compselect_callback_response (GtkDialog *dialog,
               g_assert_not_reached();
         }
 
-        if (filename == NULL) {
+        if (symbol == NULL) {
           break;
         }
+                
+	toplevel->current_clib = symbol;
         
-        component = g_path_get_basename (filename);
-        directory = g_path_get_dirname  (filename);
-        g_free (filename);
-        
-        if (toplevel->current_clib == NULL ||
-            g_ascii_strcasecmp (toplevel->current_clib, directory) != 0 ||
-            toplevel->current_basename == NULL ||
-            g_ascii_strcasecmp (toplevel->current_basename, component) != 0) {
-	  g_free (toplevel->current_clib);
-          toplevel->current_clib = directory;
-        
-          strcpy (toplevel->current_basename, component);
-	} else {
-	  g_free(directory);
-	}
-	g_free (component);
 	if (toplevel->event_state == ENDCOMP) {
           gint diff_x, diff_y;
 
@@ -142,8 +128,7 @@ x_compselect_callback_response (GtkDialog *dialog,
 	g_list_free(toplevel->page_current->complex_place_list);
 	toplevel->page_current->complex_place_list = NULL;
 	
-	o_complex_set_filename(toplevel, toplevel->current_clib,
-			       toplevel->current_basename);
+	o_complex_set_filename(toplevel, toplevel->current_clib, NULL);
         
 	toplevel->event_state = DRAWCOMP;
 
@@ -236,7 +221,7 @@ x_compselect_close (TOPLEVEL *toplevel)
 
 
 enum {
-  PROP_FILENAME=1,
+  PROP_SYMBOL=1,
   PROP_BEHAVIOR,
   PROP_HIDDEN
 };
@@ -262,10 +247,9 @@ static void compselect_get_property (GObject *object,
  *  This function determines what data is to be displayed in the
  *  component column of the selection tree.
  *
- *  Any toplevel entry of the model (that is one without parent)
- *  represents an absolute path to a directory of the component
- *  library. Only the last part of the path is displayed. Otherwise it
- *  simply copies data from the model to the column.
+ *  The top level of the model contains sources, and the next symbols.
+ *  s_clib_source_get_name() or s_clib_symbol_get_name() as
+ *  appropriate is called to get the text to display.
  *
  *  \param [in] tree_column The GtkTreeColumn.
  *  \param [in] cell        The GtkCellRenderer that is being rendered
@@ -283,16 +267,27 @@ compselect_treeview_set_cell_data (GtkTreeViewColumn *tree_column,
 {
   GtkTreeIter parent;
   GValue value = { 0, };
+  GValue strvalue = { 0, };
+  CLibSource *source;
+  CLibSymbol *symbol;
   
   gtk_tree_model_get_value (tree_model, iter, 0, &value);
+
+  g_value_init (&strvalue, G_TYPE_STRING);
+
   if (!gtk_tree_model_iter_parent (tree_model, &parent, iter)) {
-    g_value_set_string_take_ownership (
-      &value, g_path_get_basename (g_value_get_string (&value)));
+    /* If top level, must be a source. */
+    source = (CLibSource *) g_value_get_pointer (&value);
+    g_value_set_string (&strvalue, s_clib_source_get_name (source));
+  } else {
+    /* Otherwise, must be a symbol */
+    symbol = (CLibSymbol *) g_value_get_pointer (&value);
+    g_value_set_string (&strvalue, s_clib_symbol_get_name (symbol));
   }
-  g_object_set_property ((GObject*)cell, "text", &value);
+  g_object_set_property ((GObject*)cell, "text", &strvalue);
 
   g_value_unset (&value);
-  
+  g_value_unset (&strvalue);
 }
 
 /*! \brief Determines visibility of items of the treeview.
@@ -311,7 +306,8 @@ compselect_model_filter_visible_func (GtkTreeModel *model,
                                       gpointer      data)
 {
   Compselect *compselect = (Compselect*)data;
-  gchar *compname;
+  CLibSymbol *sym;
+  const gchar *compname;
   gchar *compname_upper=NULL, *text_upper=NULL;
   const gchar *text;
   gboolean ret;
@@ -323,6 +319,8 @@ compselect_model_filter_visible_func (GtkTreeModel *model,
     return TRUE;
   }
 
+  /* If this is a source, only display it if it has children that
+   * match */
   if (gtk_tree_model_iter_has_child (model, iter)) {
     GtkTreeIter iter2;
 
@@ -336,14 +334,14 @@ compselect_model_filter_visible_func (GtkTreeModel *model,
     } while (gtk_tree_model_iter_next (model, &iter2));
   } else {
     gtk_tree_model_get (model, iter,
-                        0, &compname,
+                        0, &sym,
                         -1);
+    compname = s_clib_symbol_get_name (sym);
     /* Do a case insensitive comparison, converting the strings 
        to uppercase */
     compname_upper = g_ascii_strup(compname, -1);
     text_upper = g_ascii_strup(text, -1);
     ret = (strstr (compname_upper, text_upper) != NULL);
-    g_free (compname);
     g_free(compname_upper);
     g_free(text_upper);
   }
@@ -406,30 +404,21 @@ compselect_callback_tree_selection_changed (GtkTreeSelection *selection,
   GtkTreeModel *model;
   GtkTreeIter iter, parent;
   Compselect *compselect = (Compselect*)user_data;
-  GString *ret;
-  gchar *value, *filename;
+  CLibSymbol *sym = NULL;
+  gchar *filename;
 
   if (!gtk_tree_selection_get_selected (selection, &model, &iter)) {
     return;
   }
 
-  if (gtk_tree_model_iter_has_child (model, &iter)) {
+  if (!gtk_tree_model_iter_parent (model, &parent, &iter)) {
     /* selected element is not a leaf -> not a component name */
     return;
   }
   
   /* build full path to component looking at parents */
-  gtk_tree_model_get (model, &iter, 0, &value,-1);
-  ret = g_string_new (value);
-  g_free (value);
-  while (gtk_tree_model_iter_parent (model, &parent, &iter)) {
-    gtk_tree_model_get (model, &parent, 0, &value, -1);
-    ret = g_string_prepend (ret, G_DIR_SEPARATOR_S);
-    ret = g_string_prepend (ret, value);
-    g_free (value);
-    iter = parent;
-  }
-  filename = g_string_free (ret, FALSE);
+  gtk_tree_model_get (model, &iter, 0, &sym, -1);
+  filename = s_clib_symbol_get_filename(sym);
 
   /* update the treeview with new filename */
   g_object_set (compselect->preview,
@@ -468,7 +457,7 @@ compselect_filter_timeout (gpointer data)
   
   model = gtk_tree_view_get_model (compselect->treeview);
 
- if (model != NULL) {
+  if (model != NULL) {
     gtk_tree_model_filter_refilter ((GtkTreeModelFilter*)model);
   }
 
@@ -565,34 +554,38 @@ static GtkTreeModel*
 compselect_create_child_model (void)
 {
   GtkTreeStore *store;
-  const GList *directories, *dir; 
+  GList *srchead, *srclist;
+  GList *symhead, *symlist; 
 
-  store = (GtkTreeStore*)gtk_tree_store_new (1,
-                                             G_TYPE_STRING);
+  store = (GtkTreeStore*)gtk_tree_store_new (1, G_TYPE_POINTER);
   
   /* populate component store */
-  directories = s_clib_get_directories ();
-  for (dir = directories; dir != NULL; dir = g_list_next (dir)) {
+  srchead = s_clib_get_sources ();
+  for (srclist = srchead; 
+       srclist != NULL; 
+       srclist = g_list_next (srclist)) {
+
     GtkTreeIter iter, iter2;
-    GSList *components, *comp;
 
     gtk_tree_store_append (store, &iter, NULL);
     gtk_tree_store_set (store, &iter,
-                        0, dir->data,
+                        0, srclist->data,
                         -1);
     
-    components = s_clib_get_files ((gchar*)dir->data, ".sym");
-    components = g_slist_sort (components, (GCompareFunc)g_ascii_strcasecmp);
-    for (comp = components; comp != NULL; comp = g_slist_next (comp)) {
+    symhead = s_clib_source_get_symbols ((CLibSource *)srclist->data);
+    for (symlist = symhead; 
+	 symlist != NULL; 
+	 symlist = g_list_next (symlist)) {
+
       gtk_tree_store_append (store, &iter2, &iter);
       gtk_tree_store_set (store, &iter2,
-                          0, comp->data,
+                          0, symlist->data,
                           -1);
     }
 
-    g_slist_foreach (components, (GFunc)g_free, NULL);
-    g_slist_free (components);
+    g_list_free (symhead);
   }
+  g_list_free (srchead);
 
   return (GtkTreeModel*)store;
 }
@@ -663,12 +656,11 @@ compselect_class_init (CompselectClass *klass)
   gobject_class->get_property = compselect_get_property;
 
   g_object_class_install_property (
-    gobject_class, PROP_FILENAME,
-    g_param_spec_string ("filename",
-                         "",
-                         "",
-                         NULL,
-                         G_PARAM_READABLE));
+    gobject_class, PROP_SYMBOL,
+    g_param_spec_pointer ("symbol",
+			  "",
+			  "",
+			  G_PARAM_READABLE));
   g_object_class_install_property (
     gobject_class, PROP_BEHAVIOR,
     g_param_spec_enum ("behavior",
@@ -985,31 +977,24 @@ compselect_get_property (GObject *object,
   Compselect *compselect = COMPSELECT (object);
 
   switch(property_id) {
-      case PROP_FILENAME: {
-        GtkTreeModel *model;
-        GtkTreeIter iter, parent;
-        if (gtk_tree_selection_get_selected (
-              gtk_tree_view_get_selection (compselect->treeview),
-              &model,
-              &iter)) {
-          GString *str;
-          gchar *tmp;
-          
-          gtk_tree_model_get (model, &iter, 0, &tmp, -1);
-          str = g_string_new (tmp);
-          g_free (tmp);
-          while (gtk_tree_model_iter_parent (model, &parent, &iter)) {
-            gtk_tree_model_get (model, &parent, 0, &tmp, -1);
-            str = g_string_prepend (str, G_DIR_SEPARATOR_S);
-            str = g_string_prepend (str, tmp);
-            g_free (tmp);
-            iter = parent;
-          }
-          g_value_take_string (value, g_string_free (str, FALSE));
-        } else {
-          g_value_set_string (value, NULL);
-        }
-      }
+      case PROP_SYMBOL: 
+	{
+	  GtkTreeModel *model;
+	  GtkTreeIter iter, parent;
+	  CLibSymbol *symbol;
+	  if (gtk_tree_selection_get_selected (
+		gtk_tree_view_get_selection (compselect->treeview),
+		&model,
+		&iter)
+	      && gtk_tree_model_iter_parent (model, &parent, &iter)) {
+	    
+	    gtk_tree_model_get (model, &iter, 0, &symbol, -1);
+	    g_value_set_pointer (value, symbol);
+
+	  } else {
+	    g_value_set_pointer (value, NULL);
+	  }
+	}
         break;
       case PROP_BEHAVIOR:
         g_value_set_enum (value,
diff --git a/gschem/src/x_window.c b/gschem/src/x_window.c
index edf8e15..2d22603 100644
--- a/gschem/src/x_window.c
+++ b/gschem/src/x_window.c
@@ -63,7 +63,6 @@ void x_window_setup (TOPLEVEL *toplevel)
   toplevel->current_visible = -1; /* not sure on these */
   toplevel->current_show = -1;
 
-  toplevel->internal_basename=NULL;
   toplevel->internal_clib=NULL;
 
   toplevel->series_name = NULL;
diff --git a/libgeda/include/prototype.h b/libgeda/include/prototype.h
index 0aacafc..6558f94 100644
--- a/libgeda/include/prototype.h
+++ b/libgeda/include/prototype.h
@@ -2,6 +2,7 @@
 void o_save_embedded(TOPLEVEL *w_current, OBJECT *object_list, FILE *fp);
 void o_save_write_header(FILE *fp);
 int o_save(TOPLEVEL *w_current, const char *filename);
+OBJECT *o_read_buffer(TOPLEVEL *w_current, OBJECT *object_list, char *buffer, const int size, const char *name);
 OBJECT *o_read(TOPLEVEL *w_current, OBJECT *object_list, char *filename);
 void o_scale(TOPLEVEL *w_current, OBJECT *list, int x_scale, int y_scale);
 
@@ -45,7 +46,8 @@ gint g_rc_parse_local_rc(TOPLEVEL *w_current, const gchar *rcname);
 void g_rc_parse(TOPLEVEL *w_current, const gchar* rcname, 
                 const gchar* specified_rc_filename);
 gint g_rc_parse_specified_rc(TOPLEVEL *w_current, const gchar *rcfilename);
-SCM g_rc_component_library(SCM path);
+SCM g_rc_component_library(SCM path, SCM name);
+SCM g_rc_component_library_command (SCM command, SCM name);
 SCM g_rc_component_library_search(SCM path);
 SCM g_rc_source_library(SCM path);
 SCM g_rc_source_library_search(SCM path);
@@ -162,7 +164,11 @@ ATTRIB *o_attrib_copy(ATTRIB *list);
 void o_attrib_delete(ATTRIB *a_current);
 void o_attrib_remove(ATTRIB *list, ATTRIB *remove);
 void o_attrib_detach_all(TOPLEVEL *w_current, OBJECT *object_list, OBJECT *main_head);
-OBJECT *o_read_attribs(TOPLEVEL *w_current, FILE *fp, OBJECT *object_to_get_attribs, unsigned int release_ver, unsigned int fileformat_ver);
+OBJECT *o_read_attribs(TOPLEVEL *w_current, 
+		       OBJECT *object_to_get_attribs, 
+		       TextBuffer *tb,
+		       unsigned int release_ver, 
+		       unsigned int fileformat_ver);
 void o_save_attribs(FILE *fp, ATTRIB *attribs);
 int o_attrib_get_name_value(char *string, char **name, char **value);
 void o_attrib_free_current(TOPLEVEL *w_current);
@@ -281,15 +287,22 @@ void world_get_complex_bounds(TOPLEVEL *w_current, OBJECT *complex, int *left, i
 OBJECT *add_head(void);
 int o_complex_is_eligible_attribute(TOPLEVEL *w_current, OBJECT *object, int promote_invisible);
 int o_complex_is_embedded(OBJECT *o_current);
+OBJECT *o_complex_add_by_name(TOPLEVEL *w_current, OBJECT *object_list, 
+			      GList **object_glist, char type,
+			      int color, int x, int y, int angle,
+			      int mirror, const gchar *basename,
+			      int selectable,
+			      int attribute_promotion);
 OBJECT *o_complex_add(TOPLEVEL *w_current, OBJECT *object_list, 
 		      GList **object_glist, char type, int color, 
-		      int x, int y, int angle, int mirror, char *clib, 
-		      char *basename, int selectable, int attribute_promotion);
-OBJECT *o_complex_add_embedded(TOPLEVEL *w_current, OBJECT *object_list, char type, int color, int x, int y, int angle, char *clib, char *basename, int selectable);
+		      int x, int y, int angle, int mirror, 
+		      const CLibSymbol *clib_sym, const gchar *basename,
+		      int selectable, int attribute_promotion);
+OBJECT *o_complex_add_embedded(TOPLEVEL *w_current, OBJECT *object_list, char type, int color, int x, int y, int angle, const gchar *basename, int selectable);
 void o_complex_recalc(TOPLEVEL *w_current, OBJECT *o_current);
 OBJECT *o_complex_read(TOPLEVEL *w_current, OBJECT *object_list, char buf[], unsigned int release_ver, unsigned int fileformat_ver);
 char *o_complex_save(OBJECT *object);
-void o_complex_set_filename(TOPLEVEL *w_current, char *clib, char *basename);
+void o_complex_set_filename(TOPLEVEL *w_current, const CLibSymbol *clib, char *basename);
 void o_complex_free_filename(TOPLEVEL *w_current);
 void o_complex_world_translate(TOPLEVEL *w_current, int x1, int y1, OBJECT *prim_objs);
 void o_complex_world_translate_toplevel(TOPLEVEL *w_current, int x1, int y1, OBJECT *object);
@@ -370,8 +383,11 @@ void o_net_consolidate(TOPLEVEL *w_current);
 void o_net_modify(TOPLEVEL *w_current, OBJECT *object, int x, int y, int whichone);
 
 /* o_picture.c */
-OBJECT *o_picture_read(TOPLEVEL *w_current, OBJECT *object_list, char buf[],
-            FILE *fp, unsigned int release_ver, unsigned int fileformat_ver);
+OBJECT *o_picture_read(TOPLEVEL *w_current, OBJECT *object_list,
+		       const char *first_line,
+		       TextBuffer *tb,
+		       unsigned int release_ver,
+		       unsigned int fileformat_ver);
 char *o_picture_save(OBJECT *object);
 void o_picture_set_pixbuf(TOPLEVEL *w_current, GdkPixbuf *pixbuf, char *filename);
 OBJECT *o_picture_add(TOPLEVEL *w_current, OBJECT *object_list,
@@ -426,7 +442,12 @@ int o_text_width(TOPLEVEL *w_current, char *string, int size);
 OBJECT *o_text_create_string(TOPLEVEL *w_current, OBJECT *object_list, char *string, int size, int color, int x, int y, int alignment, int angle);
 OBJECT *o_text_add(TOPLEVEL *w_current, OBJECT *object_list, char type, int color, int x, int y, int alignment, int angle, char *string, int size, int visibility, int show_name_value);
 void o_text_recalc(TOPLEVEL *w_current, OBJECT *o_current);
-OBJECT *o_text_read(TOPLEVEL *w_current, OBJECT *object_list, char buf[], FILE *fp, unsigned int release_ver, unsigned int fileformat_ver);
+OBJECT *o_text_read(TOPLEVEL *w_current, 
+		    OBJECT *object_list, 
+		    const char *first_line,
+		    TextBuffer *tb,
+		    unsigned int release_ver,
+		    unsigned int fileformat_ver);
 void o_text_set_info_font(char buf[]);
 char *o_text_save(OBJECT *object);
 void o_text_recreate(TOPLEVEL *w_current, OBJECT *o_current);
@@ -472,15 +493,26 @@ char *remove_nl(char *string);
 char *remove_last_nl(char *string);
 char *remove_string(char *string, int start, int end);
 char *insert_string(char *string, int start, char *insert_string);
+char *buffer_next_line(const char *buffer, const size_t size, size_t *offset);
 char *expand_env_variables(char *string);
 
 /* s_clib.c */
 void s_clib_init (void);
 void s_clib_free (void);
-void s_clib_add_directory (const gchar *directory);
-const GList* s_clib_get_directories ();
-GSList* s_clib_get_files (const gchar *directory, const gchar *filter);
-const GSList* s_clib_search_basename (const gchar *basename);
+GList *s_clib_get_sources ();
+void s_clib_refresh ();
+const CLibSource *s_clib_add_directory (const gchar *directory, 
+					const gchar *name);
+const CLibSource *s_clib_add_command (const gchar *command,
+				      const gchar *name);
+const gchar *s_clib_source_get_name (const CLibSource *source);
+GList *s_clib_source_get_symbols (const CLibSource *source);
+const gchar *s_clib_symbol_get_name (const CLibSymbol *symbol);
+gchar *s_clib_symbol_get_filename (const CLibSymbol *symbol);
+const CLibSource *s_clib_symbol_get_source (const CLibSymbol *symbol);
+gchar *s_clib_symbol_get_data (const CLibSymbol *symbol);
+GList *s_clib_glob (const gchar *glob);
+gchar *s_clib_symbol_get_data_by_name (const gchar *name);
 
 /* s_color.c */
 void s_color_init(void);
@@ -523,7 +555,7 @@ gchar* s_encoding_base64_decode (gchar* src, guint srclen, guint* dstlenp);
 /* s_hierarchy.c */
 int s_hierarchy_down_schematic_single(TOPLEVEL *w_current, const gchar *filename, PAGE *parent, int page_control, int flag);
 void s_hierarchy_down_schematic_multiple (TOPLEVEL *w_current, const gchar *filename, PAGE *parent);
-void s_hierarchy_down_symbol (TOPLEVEL *w_current, const gchar *filename, PAGE *parent);
+void s_hierarchy_down_symbol (TOPLEVEL *w_current, const CLibSymbol *symbol, PAGE *parent);
 void s_hierarchy_up(TOPLEVEL *w_current, int pid);
 GList* s_hierarchy_traversepages(TOPLEVEL *w_current, gint flags);
 gint s_hierarchy_print_page(PAGE *p_current, void * data);
@@ -635,3 +667,10 @@ char *u_basic_breakup_string(char *string, char delimiter, int count);
 void u_basic_strip_trailing(char *string, char c);
 int u_basic_has_trailing(char *string, char c);
 int u_basic_count_char(const char *string, char character);
+
+/* s_textbuffer.c */
+TextBuffer *s_textbuffer_new (gchar *data, const gint size);
+TextBuffer *s_textbuffer_free (TextBuffer *tb);
+void s_textbuffer_seek (TextBuffer *tb, const gint offset);
+gchar *s_textbuffer_next (TextBuffer *tb, const gsize count);
+gchar *s_textbuffer_next_line (TextBuffer *tb);
diff --git a/libgeda/include/struct.h b/libgeda/include/struct.h
index 8b4e9c8..82fdc26 100644
--- a/libgeda/include/struct.h
+++ b/libgeda/include/struct.h
@@ -55,6 +55,12 @@ typedef struct st_net NET;
 typedef struct st_schcheck SCHCHECK;
 typedef struct st_chkerrs CHKERRS;
 
+/* Managed text buffers */
+typedef struct _TextBuffer TextBuffer;
+
+/* Component library objects */
+typedef struct _CLibSource CLibSource;
+typedef struct _CLibSymbol CLibSymbol;
 
 /* PB : change begin */
 /* PB : these enum are constant to define :
@@ -206,8 +212,9 @@ struct st_object {
 	
   int visited;		/* used in gnetlist for travesal purposes */
 
-  char *complex_basename;			/* Complex basename */
-  char *complex_clib;			/* Complex Component Library */
+  gboolean complex_embedded;                    /* is embedded component? */
+  gchar *complex_basename;              /* Component Library Symbol name */
+  const CLibSymbol *complex_clib;	/* Component Library Symbol */
   OBJECT *complex_parent;		/* Complex parent object pointer */
   /* used only in complex head nodes */
 
@@ -414,8 +421,7 @@ struct st_toplevel {
   /* if it should go in here or not */
   /* leave outside for now */
 
-  char *internal_basename;		
-  char *internal_clib;     
+  const CLibSymbol *internal_clib;     
   /* have to decided on component list stuff */
   /* if it should go in here or not */
   /* leave outside for now */
@@ -544,9 +550,7 @@ struct st_toplevel {
   GtkWidget *cswindow;			/* component select */
   GtkWidget *clib_list;
   GtkWidget *basename_list;
-  char *current_clib;
-  char current_basename[256]; 	
-
+  CLibSymbol *current_clib;
 
   GtkWidget *iwindow;			/* image write dialog box */
   GtkWidget *ifilename_entry; 
diff --git a/libgeda/src/Makefile.am b/libgeda/src/Makefile.am
index af773ab..ed03681 100644
--- a/libgeda/src/Makefile.am
+++ b/libgeda/src/Makefile.am
@@ -20,7 +20,7 @@ libgeda_la_SOURCES = \
 	o_pin_basic.c o_image.c o_embed.c \
 	u_basic.c s_attrib.c s_basic.c \
 	s_clib.c s_encoding.c s_hierarchy.c s_papersizes.c s_stretch.c \
-	s_log.c \
+	s_log.c s_textbuffer.c \
 	s_page.c s_slib.c s_color.c s_undo.c s_conn.c \
 	s_cue.c s_tile.c s_menu.c s_toplevel.c g_smob.c libgeda.c \
 	g_register.c g_rc.c i_vars.c o_picture.c gdk-pixbuf-hacks.c
diff --git a/libgeda/src/a_basic.c b/libgeda/src/a_basic.c
index 1b0e8e5..d19ca70 100644
--- a/libgeda/src/a_basic.c
+++ b/libgeda/src/a_basic.c
@@ -92,7 +92,7 @@ void o_save_embedded(TOPLEVEL *w_current, OBJECT *object_list, FILE *fp)
 
           case(OBJ_COMPLEX):
             out = (char *) o_complex_save(o_current);
-            if (strncmp(o_current->complex_clib, "EMBEDDED", 8) == 0) {
+            if (o_complex_is_embedded(o_current)) {
               fprintf(fp, "[\n");
 								
               o_save_embedded(
@@ -225,7 +225,7 @@ int o_save(TOPLEVEL *w_current, const char *filename)
             fprintf(fp, "%s\n", out);
             already_wrote=1;
 	    g_free(out); /* need to free here because of the above flag */
-            if (strncmp(o_current->complex_clib, "EMBEDDED", 8) == 0) {
+            if (o_complex_is_embedded(o_current)) {
               fprintf(fp, "[\n");
 								
               o_save_embedded(
@@ -288,19 +288,30 @@ int o_save(TOPLEVEL *w_current, const char *filename)
   return 1;
 }
 
-/*! \brief Read a file
+/*! \brief Read a memory buffer
  *  \par Function Description
- *  This function reads a file in libgead format.
+ *  This function reads data in libgeda format from a memory buffer.
+ *
+ *  If the size argument is negative, the buffer is assumed to be
+ *  null-terminated.
+ *
+ *  The name argument is used for debugging, and should be set to a
+ *  meaningful string (e.g. the name of the file the data is from).
  *
  *  \param [in,out] w_current    The current TOPLEVEL structure.
  *  \param [in]     object_list  The object_list to read data to.
- *  \param [in]     filename     The filename to read from.
+ *  \param [in]     buffer       The memory buffer to read from.
+ *  \param [in]     size         The size of the buffer.
+ *  \param [in]     name         The name to describe the data with.
  *  \return object_list if successful read, or NULL on error.
  */
-OBJECT *o_read(TOPLEVEL *w_current, OBJECT *object_list, char *filename)
+OBJECT *o_read_buffer(TOPLEVEL *w_current, OBJECT *object_list, 
+		      char *buffer, const int size, 
+		      const char *name)
 {
-  FILE *fp;
-  char buf[1024];
+  char *line = NULL;
+  TextBuffer *tb = NULL;
+
   char objtype;
   OBJECT *object_list_save=NULL;
   OBJECT *temp_tail=NULL;
@@ -315,19 +326,24 @@ OBJECT *o_read(TOPLEVEL *w_current, OBJECT *object_list, char *filename)
 
   int embedded_level = 0;
 
+
   /* fill version with default file format version (the current one) */
   current_fileformat_ver = FILEFORMAT_VERSION;
 
-  fp = fopen(filename, "r");
-
-  if (fp == NULL) {
-    s_log_message("o_read: Could not open [%s]\n", filename);
+  if (buffer == NULL) {
+    s_log_message("o_read_buffer: Received NULL buffer\n");
     return(NULL);
   }
 
-  while ( fgets(buf, 1024, fp) != NULL) {
+  tb = s_textbuffer_new (buffer, size);
+  g_assert (tb != NULL);
+
+  while (1) {
 
-    sscanf(buf, "%c", &objtype);
+    line = s_textbuffer_next_line(tb);
+    if (line == NULL) break;
+
+    sscanf(line, "%c", &objtype);
 
     /* Do we need to check the symbol version?  Yes, but only if */
     /* 1) the last object read was a complex and */
@@ -346,40 +362,42 @@ OBJECT *o_read(TOPLEVEL *w_current, OBJECT *object_list, char *filename)
     switch (objtype) {
 
       case(OBJ_LINE):
-        object_list = (OBJECT *) o_line_read(w_current, object_list, buf, 
+        object_list = (OBJECT *) o_line_read(w_current, object_list, line, 
 	                                     release_ver, fileformat_ver);
         break;
 
 
       case(OBJ_NET):
-        object_list = (OBJECT *) o_net_read(w_current, object_list, buf,
+        object_list = (OBJECT *) o_net_read(w_current, object_list, line,
                                             release_ver, fileformat_ver);
         break;
 
       case(OBJ_BUS):
-        object_list = (OBJECT *) o_bus_read(w_current, object_list, buf,
+        object_list = (OBJECT *) o_bus_read(w_current, object_list, line,
                                             release_ver, fileformat_ver);
         break;
 
       case(OBJ_BOX):
-        object_list = (OBJECT *) o_box_read(w_current, object_list, buf,
+        object_list = (OBJECT *) o_box_read(w_current, object_list, line,
                                             release_ver, fileformat_ver);
         break;
 		
       case(OBJ_PICTURE):
-        object_list = (OBJECT *) o_picture_read(w_current, object_list, buf,
-						fp,
+	line = g_strdup(line);
+        object_list = (OBJECT *) o_picture_read(w_current, object_list, 
+						line, tb,
 						release_ver, fileformat_ver);
+	g_free (line);
         break;
 		
       case(OBJ_CIRCLE):
-        object_list = (OBJECT *) o_circle_read(w_current, object_list, buf,
+        object_list = (OBJECT *) o_circle_read(w_current, object_list, line,
                                                release_ver, fileformat_ver);
         break;
 
       case(OBJ_COMPLEX):
       case(OBJ_PLACEHOLDER):
-        object_list = (OBJECT *) o_complex_read(w_current, object_list, buf,
+        object_list = (OBJECT *) o_complex_read(w_current, object_list, line,
                                                 release_ver, fileformat_ver);
 
         /* this is necessary because complex may add attributes which float */
@@ -391,20 +409,21 @@ OBJECT *o_read(TOPLEVEL *w_current, OBJECT *object_list, char *filename)
         break;
 
       case(OBJ_TEXT):
-        /* fgets(string, 1024, fp); string lines are now read inside: */
-        object_list = (OBJECT *) o_text_read(w_current, object_list, buf,
-                                             fp,
+	line = g_strdup(line);
+        object_list = (OBJECT *) o_text_read(w_current, object_list, 
+					     line, tb,
                                              release_ver, fileformat_ver);
+	g_free(line);
         break;
 
       case(OBJ_PIN):
-        object_list = (OBJECT *) o_pin_read(w_current, object_list, buf,
+        object_list = (OBJECT *) o_pin_read(w_current, object_list, line,
                                             release_ver, fileformat_ver);
         found_pin++;
         break;
 
       case(OBJ_ARC):
-        object_list = (OBJECT *) o_arc_read(w_current, object_list, buf,
+        object_list = (OBJECT *) o_arc_read(w_current, object_list, line,
                                             release_ver, fileformat_ver);
         break;
 
@@ -412,7 +431,8 @@ OBJECT *o_read(TOPLEVEL *w_current, OBJECT *object_list, char *filename)
         object_before_attr = object_list;
 	/* first is the fp */
 	/* 2nd is the object to get the attributes */ 
-        object_list = (OBJECT *) o_read_attribs(w_current, fp, object_list,
+        object_list = (OBJECT *) o_read_attribs(w_current, object_list,
+						tb,
                                                 release_ver, fileformat_ver);
 
         /* by now we have finished reading all the attributes */
@@ -452,7 +472,7 @@ OBJECT *o_read(TOPLEVEL *w_current, OBJECT *object_list, char *filename)
 	} else {
         	fprintf(stderr, "Read unexpected embedded "
 				"symbol start marker in [%s] :\n>>\n%s<<\n", 
-				filename, buf);
+				name, line);
 	}
        	break;
 
@@ -472,7 +492,7 @@ OBJECT *o_read(TOPLEVEL *w_current, OBJECT *object_list, char *filename)
 	} else {
         	fprintf(stderr, "Read unexpected embedded "
 				"symbol end marker in [%s] :\n>>\n%s<<\n", 
-				filename, buf);
+				name, line);
 	}
 	
         break;
@@ -482,7 +502,7 @@ OBJECT *o_read(TOPLEVEL *w_current, OBJECT *object_list, char *filename)
         break;	
 
       case(INFO_FONT): 
-        o_text_set_info_font(buf);
+        o_text_set_info_font(line);
         break;	
 
       case(COMMENT):
@@ -490,7 +510,7 @@ OBJECT *o_read(TOPLEVEL *w_current, OBJECT *object_list, char *filename)
         break;
 
       case(VERSION_CHAR):
-        itemsread = sscanf(buf, "v %u %u\n", &release_ver, &fileformat_ver);
+        itemsread = sscanf(line, "v %u %u\n", &release_ver, &fileformat_ver);
 
 	/* 20030921 was the last version which did not have a fileformat */
 	/* version.  The below latter test should not happen, but it is here */
@@ -503,18 +523,17 @@ OBJECT *o_read(TOPLEVEL *w_current, OBJECT *object_list, char *filename)
 	if (fileformat_ver < current_fileformat_ver)
         {
        	  s_log_message("Read an old format sym/sch file!\n"
-                        "Please run g[sym|sch]update on:\n[%s]\n", filename);
+                        "Please run g[sym|sch]update on:\n[%s]\n", name);
 	}
         break;
 
       default:
         fprintf(stderr, "Read garbage in [%s] :\n>>\n%s<<\n",
-                filename, buf);
+                name, line);
         break;
     }
 
   }
-  fclose(fp);
 
   /* Was the very last thing we read a complex and has it not been checked */
   /* yet?  This would happen if the complex is at the very end of the file  */
@@ -530,8 +549,48 @@ OBJECT *o_read(TOPLEVEL *w_current, OBJECT *object_list, char *filename)
       o_pin_update_whichend(w_current, return_head(object_list), found_pin);
     }
   }
+
+  tb = s_textbuffer_free(tb);
   
   return(object_list);
+
+}
+
+/*! \brief Read a file
+ *  \par Function Description
+ *  This function reads a file in libgead format.
+ *
+ *  \param [in,out] w_current    The current TOPLEVEL structure.
+ *  \param [in]     object_list  The object_list to read data to.
+ *  \param [in]     filename     The filename to read from.
+ *  \return object_list if successful read, or NULL on error.
+ */
+OBJECT *o_read(TOPLEVEL *w_current, OBJECT *object_list, char *filename)
+{
+  GError *err = NULL;
+  char *buffer = NULL;
+  size_t size;
+  OBJECT *result = NULL;
+
+  g_file_get_contents(filename, &buffer, &size, &err);
+
+  g_assert ((buffer == NULL && err != NULL) 
+	    || (buffer != NULL && err == NULL));
+
+  if (err != NULL)
+    {
+      /* Report error to user, and free error */
+      g_assert (buffer == NULL);
+      fprintf (stderr, "o_read: Unable to read file: [%s]\n", err->message);
+      g_error_free (err);
+      return NULL;
+    } 
+
+  /* Parse file contents */
+  g_assert (buffer != NULL);
+  result = o_read_buffer (w_current, object_list, buffer, size, filename);
+  g_free (buffer);
+  return result;
 }
 
 /*! \brief Scale a set of lines.
diff --git a/libgeda/src/g_rc.c b/libgeda/src/g_rc.c
index 8eb958f..4dae063 100644
--- a/libgeda/src/g_rc.c
+++ b/libgeda/src/g_rc.c
@@ -341,16 +341,24 @@ void g_rc_parse(TOPLEVEL *w_current,
 /*! \brief
  *  \par Function Description
  *
- *  \param [in] path  
+ *  \param [in] path 
+ *  \param [in] name Optional descriptive name for library directory.
  *  \return SCM_BOOL_T on success, SCM_BOOL_F otherwise.
  */
-SCM g_rc_component_library(SCM path)
+SCM g_rc_component_library(SCM path, SCM name)
 {
   char *string;
+  char *namestr = NULL;
 
   SCM_ASSERT (SCM_NIMP (path) && SCM_STRINGP (path), path,
               SCM_ARG1, "component-library");
   
+  if (name != SCM_UNDEFINED) {
+    SCM_ASSERT (SCM_NIMP (name) && SCM_STRINGP (name), name,
+		SCM_ARG2, "component-library");
+    namestr = SCM_STRING_CHARS (name);
+  }
+  
   string = g_strdup (SCM_STRING_CHARS (path));
   /* take care of any shell variables */
   string = expand_env_variables(string);
@@ -365,7 +373,7 @@ SCM g_rc_component_library(SCM path)
   }
 
   if (g_path_is_absolute (string)) {
-    s_clib_add_directory (string);
+    s_clib_add_directory (string, namestr);
   } else {
     gchar *cwd = g_get_current_dir ();
     gchar *temp;
@@ -373,7 +381,7 @@ SCM g_rc_component_library(SCM path)
     u_basic_strip_trailing(cwd, G_DIR_SEPARATOR);
 #endif
     temp = g_strconcat (cwd, G_DIR_SEPARATOR_S, string, NULL);
-    s_clib_add_directory (temp);
+    s_clib_add_directory (temp, namestr);
     g_free(temp);
     g_free(cwd);
   }
@@ -385,6 +393,39 @@ SCM g_rc_component_library(SCM path)
   return SCM_BOOL_T;
 }
 
+/*! \brief Guile callback for adding library commands.
+ *  \par Function Description
+ *  Callback function for the "component-library-command" Guile
+ *  function, which can be used in the rc files to add a command to
+ *  the component library.
+ *
+ *  \param [in] command Command to add.
+ *  \param [in] name    Optional descriptive name for component source.
+ *  \return SCM_BOOL_T on success, SCM_BOOL_F otherwise.
+ */
+SCM g_rc_component_library_command (SCM command, SCM name)
+{
+  gchar *namestr = NULL;
+  gchar *cmdstr = NULL;
+  SCM_ASSERT (SCM_STRINGP (command), command, SCM_ARG1, 
+	      "component-library-command");
+  cmdstr = g_strdup(SCM_STRING_CHARS (command));
+
+  if (name != SCM_UNDEFINED) {
+    SCM_ASSERT (SCM_STRINGP (name), name, SCM_ARG2, 
+	      "component-library-command");
+    namestr = SCM_STRING_CHARS (name);
+  }
+  /* take care of any shell variables */
+  cmdstr = expand_env_variables(cmdstr);
+
+  s_clib_add_command (cmdstr, namestr);
+
+  g_free (cmdstr);
+
+  return SCM_BOOL_T;
+}
+
 /*! \todo Finish function description!!!
  *  \brief
  *  \par Function Description
@@ -436,7 +477,7 @@ SCM g_rc_component_library_search(SCM path)
 
       if (g_file_test (fullpath, G_FILE_TEST_IS_DIR)) {
         if (g_path_is_absolute (fullpath)) {
-          s_clib_add_directory (fullpath);
+          s_clib_add_directory (fullpath, NULL);
         } else {
           gchar *cwd = g_get_current_dir ();
           gchar *temp;
@@ -447,7 +488,7 @@ SCM g_rc_component_library_search(SCM path)
                               G_DIR_SEPARATOR_S,
                               fullpath,
                               NULL);
-          s_clib_add_directory (temp);
+          s_clib_add_directory (temp, NULL);
           g_free(temp);
           g_free(cwd);
         }
diff --git a/libgeda/src/g_register.c b/libgeda/src/g_register.c
index 0228879..b7913ff 100644
--- a/libgeda/src/g_register.c
+++ b/libgeda/src/g_register.c
@@ -57,7 +57,8 @@ struct gsubr_t {
 
 /*! \brief */
 static struct gsubr_t libgeda_funcs[] = {
-  { "component-library",        1, 0, 0, g_rc_component_library },
+  { "component-library",        1, 1, 0, g_rc_component_library },
+  { "component-library-command", 1, 1, 0, g_rc_component_library_command },
   { "component-library-search", 1, 0, 0, g_rc_component_library_search },
   { "source-library",           1, 0, 0, g_rc_source_library },
   { "source-library-search",    1, 0, 0, g_rc_source_library_search },
diff --git a/libgeda/src/o_attrib.c b/libgeda/src/o_attrib.c
index 49cb79c..a0380ab 100644
--- a/libgeda/src/o_attrib.c
+++ b/libgeda/src/o_attrib.c
@@ -647,37 +647,42 @@ void o_attrib_detach_all(TOPLEVEL *w_current, OBJECT *object_list, OBJECT *main_
 #endif
 }
 
-/*! \brief Read attributes from a file.
+/*! \brief Read attributes from a buffer.
  *  \par Function Description
- *  Read attributes from a file.
+ *  Read attributes from a TextBuffer.
  *
  *  \param [in]  w_current              The TOPLEVEL object.
- *  \param [in]  fp                     FILE pointer to read from.
  *  \param [out] object_to_get_attribs  Storage for attributes.
+ *  \param [in]  tb                     The text buffer to read from.
  *  \param [in]  release_ver            libgeda release version number.
  *  \param [in]  fileformat_ver         file format version number.
  *  \return Pointer to object_to_get_attribs.
  */
-OBJECT *o_read_attribs(TOPLEVEL *w_current, FILE *fp, OBJECT *object_to_get_attribs,
+OBJECT *o_read_attribs(TOPLEVEL *w_current, 
+		       OBJECT *object_to_get_attribs,
+   		       TextBuffer *tb,
 		       unsigned int release_ver, unsigned int fileformat_ver)
 {
   OBJECT *object_list=NULL;
-  char buf[1024];
+  char *line = NULL;
   char objtype;
   int ATTACH=FALSE;
   int saved_color = -1;
 
   object_list = object_to_get_attribs;
 
-  while ( fgets(buf, 1024, fp) != NULL) {
+  while (1) {
 
-    sscanf(buf, "%c", &objtype);
+    line = s_textbuffer_next_line (tb);
+    if (line == NULL) break;
+
+    sscanf(line, "%c", &objtype);
     switch (objtype) {
 
       case(OBJ_LINE):
         object_list = (OBJECT *) o_line_read(w_current, 
                                              object_list,
-                                             buf, 
+                                             line, 
                                              release_ver, fileformat_ver);
         break;
 
@@ -685,21 +690,21 @@ OBJECT *o_read_attribs(TOPLEVEL *w_current, FILE *fp, OBJECT *object_to_get_attr
       case(OBJ_NET):
         object_list = (OBJECT *) o_net_read(w_current, 
                                             object_list, 
-                                            buf, 
+                                            line, 
                                             release_ver, fileformat_ver);
         break;
 
       case(OBJ_BUS):
         object_list = (OBJECT *) o_bus_read(w_current, 
                                             object_list, 
-                                            buf, 
+                                            line, 
                                             release_ver, fileformat_ver);
         break;
 
       case(OBJ_BOX):
         object_list = (OBJECT *) o_box_read(w_current, 
                                             object_list, 
-                                            buf, 
+                                            line, 
                                             release_ver, fileformat_ver);
         break;
 		
@@ -707,7 +712,7 @@ OBJECT *o_read_attribs(TOPLEVEL *w_current, FILE *fp, OBJECT *object_to_get_attr
         object_list = (OBJECT *) o_circle_read(
                                                w_current, 
                                                object_list, 
-                                               buf, 
+                                               line, 
                                                release_ver, fileformat_ver);
         break;
 
@@ -717,7 +722,7 @@ OBJECT *o_read_attribs(TOPLEVEL *w_current, FILE *fp, OBJECT *object_to_get_attr
         object_list = (OBJECT *) o_complex_read(
                                                 w_current, 
                                                 object_list, 
-                                                buf, 
+                                                line, 
                                                 release_ver, fileformat_ver);
 
 				/* this is necessary because complex may add
@@ -730,24 +735,25 @@ OBJECT *o_read_attribs(TOPLEVEL *w_current, FILE *fp, OBJECT *object_to_get_attr
       case(OBJ_PIN):
         object_list = (OBJECT *) o_pin_read(w_current, 
                                             object_list, 
-                                            buf, 
+                                            line, 
                                             release_ver, fileformat_ver);
         break;
 
       case(OBJ_ARC):
         object_list = (OBJECT *) o_arc_read(w_current, 
                                             object_list, 
-                                            buf, 
+                                            line, 
                                             release_ver, fileformat_ver);
         break;
 
       case(OBJ_TEXT):
-        /* fgets(string, 1024, fp); text strings are now read inside: */
+	line = g_strdup (line);
         object_list = (OBJECT *) o_text_read(w_current,
                                              object_list, 
-                                             buf, 
-                                             fp, 
+                                             line,
+					     tb,
                                              release_ver, fileformat_ver);
+	g_free (line);
         saved_color = object_list->color;
         ATTACH=TRUE;
 		
diff --git a/libgeda/src/o_complex_basic.c b/libgeda/src/o_complex_basic.c
index 03b6e1a..065b59e 100644
--- a/libgeda/src/o_complex_basic.c
+++ b/libgeda/src/o_complex_basic.c
@@ -308,8 +308,7 @@ int o_complex_is_embedded(OBJECT *o_current)
   if(o_current->complex == NULL)
   return 0;
 
-  if (o_current->complex_clib && 
-      strncmp(o_current->complex_clib, "EMBEDDED", 8) == 0) {
+  if (o_current->complex_embedded) {
     return 1;
   } else {
     return 0;
@@ -317,6 +316,46 @@ int o_complex_is_embedded(OBJECT *o_current)
 
 }
 
+/*! \brief Add a symbol given its basename.
+ *  \todo Complete function documentation
+ */
+OBJECT *o_complex_add_by_name(TOPLEVEL *w_current, OBJECT *object_list, 
+			      GList **object_glist, char type,
+			      int color, int x, int y, int angle,
+			      int mirror, const gchar *basename,
+			      int selectable,
+			      int attribute_promotion)
+{
+  const CLibSymbol *sym;
+  GList *symlist;
+
+  symlist = s_clib_glob (basename);
+
+  if (symlist == NULL) {
+    s_log_message("Component [%s] was not found in any component library\n", 
+		  basename);
+    fprintf(stderr,
+	    "Component [%s] was not found in any component library\n", 
+	    basename);
+    sym = NULL;
+  } else {
+    if (g_list_next (symlist) != NULL) {
+      s_log_message ("More than one component found with name [%s]\n",
+		     basename);
+    }
+    /*! \todo For now, use the first source with a symbol with that
+     *  name. Maybe open a dialog to select the right one? */
+    sym = (CLibSymbol *) symlist->data;
+  }
+
+  g_list_free (symlist);
+
+  return o_complex_add (w_current, object_list, object_glist, type,
+			color, x, y, angle, mirror, sym, basename,
+			selectable, attribute_promotion);
+
+}
+
 /* Done */
 /*! \brief
  *  \par Function Description
@@ -325,8 +364,9 @@ int o_complex_is_embedded(OBJECT *o_current)
 OBJECT *o_complex_add(TOPLEVEL *w_current, OBJECT *object_list, 
 		      GList **object_glist, char type,
 		      int color, int x, int y, int angle,
-		      int mirror, char *clib,
-		      char *basename, int selectable,
+		      int mirror, const CLibSymbol *clib,
+		      const gchar *basename,
+		      int selectable,
 		      int attribute_promotion)
 {
   OBJECT *new_node=NULL;
@@ -336,9 +376,10 @@ OBJECT *o_complex_add(TOPLEVEL *w_current, OBJECT *object_list,
   int save_adding_sel = 0;
   int loaded_normally = FALSE;
   gboolean use_object_list;
-  char *filename;
   GList *glist_ptr;
 
+  gchar *buffer;
+
   if (object_list) {
     use_object_list = TRUE;
   } else {
@@ -348,11 +389,15 @@ OBJECT *o_complex_add(TOPLEVEL *w_current, OBJECT *object_list,
   new_node = s_basic_init_object("complex");
   new_node->type = type;
 
-  new_node->complex_basename = g_strdup(basename);
-  if (clib)
-    new_node->complex_clib = g_strdup(clib);
-  else
-    new_node->complex_clib = NULL;
+  new_node->complex_clib = clib;
+  if (clib != NULL) {
+    new_node->complex_basename = g_strdup (s_clib_symbol_get_name (clib));
+  } else {
+    new_node->complex_basename = g_strdup (basename);
+  }
+
+
+  new_node->complex_embedded = FALSE;
 
   new_node->color = color;
 	
@@ -389,30 +434,21 @@ OBJECT *o_complex_add(TOPLEVEL *w_current, OBJECT *object_list,
   w_current->page_current->object_parent = prim_objs;
   /* reason this works is because it has a head, see add_head above */
 
-  /* check for validity */
-  if (clib && basename)
-    filename = g_strdup_printf("%s%c%s", clib, G_DIR_SEPARATOR, basename);
-  else 
-    filename = g_strdup("unknown");
+  /* get the symbol data */
+  if (clib != NULL) {
+    buffer = s_clib_symbol_get_data (clib);
+  }
 
   save_adding_sel = w_current->ADDING_SEL;
   w_current->ADDING_SEL = 1;	/* name is hack, don't want to */
 
-  if (access(filename, R_OK)) {
+  if (clib == NULL || buffer == NULL) {
 
     OBJECT *save_prim_objs;
     char *not_found_text = NULL;
     int left, right, top, bottom;
     int x_offset, y_offset;
 
-    if (clib == NULL) {
-      s_log_message("Component library was not found or specified\n");
-    } else if (basename == NULL) {
-      s_log_message("Basename (component) was not found or specified\n");
-    } else {
-      s_log_message("Could not open symbol file [%s]\n", filename); 
-    } 
-
     /* filename was NOT found */
     loaded_normally = FALSE;
 
@@ -434,7 +470,8 @@ OBJECT *o_complex_add(TOPLEVEL *w_current, OBJECT *object_list,
 
     /* Add some useful text */
     not_found_text = 
-      g_strdup_printf ("Component not found:\n %s", basename);
+      g_strdup_printf ("Component not found:\n %s", 
+		       new_node->complex_basename);
     prim_objs = o_text_add(w_current, prim_objs,
                            OBJ_TEXT, DETACHED_ATTRIBUTE_COLOR, 
                            x + NOT_FOUND_TEXT_X, 
@@ -488,7 +525,9 @@ OBJECT *o_complex_add(TOPLEVEL *w_current, OBJECT *object_list,
     loaded_normally = TRUE;
     
     /* add connections till translated */
-    o_read(w_current, prim_objs, filename);
+    o_read_buffer(w_current, prim_objs, buffer, -1, new_node->complex_basename);
+
+    g_free (buffer);
     
   }
   w_current->ADDING_SEL = save_adding_sel;
@@ -618,7 +657,6 @@ OBJECT *o_complex_add(TOPLEVEL *w_current, OBJECT *object_list,
   else
     o_complex_recalc(w_current, new_node);
 
-  g_free(filename);
   return(object_list);
 }
 
@@ -628,7 +666,7 @@ OBJECT *o_complex_add(TOPLEVEL *w_current, OBJECT *object_list,
  */
 OBJECT *o_complex_add_embedded(TOPLEVEL *w_current, OBJECT *object_list,
 			       char type, int color, int x, int y, int angle,
-			       char *clib, char *basename, int selectable)
+			       const gchar *basename, int selectable)
 {
   OBJECT *prim_objs=NULL;
   OBJECT *new_node=NULL;
@@ -643,11 +681,10 @@ OBJECT *o_complex_add_embedded(TOPLEVEL *w_current, OBJECT *object_list,
   new_node->complex->angle = angle;
   new_node->complex->mirror = 0;
 	
+  new_node->complex_clib = NULL;
   new_node->complex_basename = g_strdup(basename);
-  if (clib)
-    new_node->complex_clib = g_strdup(clib);
-  else
-    new_node->complex_clib = NULL;
+
+  new_node->complex_embedded = TRUE;
 
   new_node->color = color;
 
@@ -700,6 +737,7 @@ void o_complex_recalc(TOPLEVEL *w_current, OBJECT *o_current)
 /*! \brief
  *  \par Function Description
  *
+ *  \todo Don't use fixed-length string for symbol basename
  */
 OBJECT *o_complex_read(TOPLEVEL *w_current, OBJECT *object_list,
 		       char buf[], unsigned int release_ver,
@@ -709,8 +747,7 @@ OBJECT *o_complex_read(TOPLEVEL *w_current, OBJECT *object_list,
   int x1, y1;
   int angle;
 
-  char basename[256]; /* hack */
-  char *clib=NULL;
+  char basename[256]; /* FIXME This is a hack */
 	
   int selectable;
   int mirror;
@@ -749,32 +786,15 @@ OBJECT *o_complex_read(TOPLEVEL *w_current, OBJECT *object_list,
   object_list = o_complex_add_embedded(w_current, 
                                        object_list, type, 
                                        WHITE, x1, y1, angle,
-                                       "EMBEDDED", basename, 
+                                       basename + 8, 
                                        selectable);
   } else {
-    const GSList *clibs = s_clib_search_basename (basename);
-    if (clibs == NULL) {
-      s_log_message("Component [%s] was not found in any component library\n", 
-		    basename);
-      fprintf(stderr,
-	      "Component [%s] was not found in any component library\n", 
-	      basename);
-      clib = NULL;
-    } else {
-      if (g_slist_next (clibs)) {
-	s_log_message ("More than one component found with name [%s]\n",
-		       basename);
-	/* PB: for now, use the first directory in clibs */
-	/* PB: maybe open a dialog to select the right one? */
-      }
-      clib = (gchar*)clibs->data;
-    }
     
-    object_list = o_complex_add(w_current, object_list, NULL, type, 
-				WHITE, 
-				x1, y1, 
-				angle, mirror,
-				clib, basename, selectable, FALSE);
+    object_list = o_complex_add_by_name(w_current, object_list, NULL, type, 
+					WHITE, 
+					x1, y1, 
+					angle, mirror,
+					basename, selectable, FALSE);
   }
 
   return object_list;
@@ -788,6 +808,7 @@ char *o_complex_save(OBJECT *object)
 {
   int selectable;
   char *buf = NULL;
+  char *basename;
 
   g_return_val_if_fail (object != NULL, NULL);
 
@@ -796,16 +817,28 @@ char *o_complex_save(OBJECT *object)
   else 
   selectable = 0;
 
-  if (object->type == OBJ_COMPLEX) {
-    buf = g_strdup_printf("%c %d %d %d %d %d %s", object->type,
-	    		  object->complex->x, object->complex->y, selectable, 
-			  object->complex->angle, object->complex->mirror, 
-			  object->complex_basename);
-  } else if (object->type == OBJ_PLACEHOLDER) {
-    buf = g_strdup_printf("C %d %d %d %d %d %s",
-	    		  object->complex->x, object->complex->y, selectable, 
-			  object->complex->angle, object->complex->mirror, 
-			  object->complex_basename);  /* write 'C' manually */
+  if ((object->type == OBJ_COMPLEX) || (object->type == OBJ_PLACEHOLDER)) {
+    basename = g_strdup_printf ("%s%s",
+				object->complex_embedded ? "EMBEDDED" : "",
+				object->complex_basename);
+    switch (object->type) {
+    case OBJ_COMPLEX:
+      buf = g_strdup_printf("%c %d %d %d %d %d %s", object->type,
+			    object->complex->x, object->complex->y, 
+			    selectable, object->complex->angle, 
+			    object->complex->mirror, basename);
+      break;
+    case OBJ_PLACEHOLDER:
+      /* write 'C' manually */
+      buf = g_strdup_printf("C %d %d %d %d %d %s",
+			    object->complex->x, object->complex->y, 
+			    selectable, object->complex->angle, 
+			    object->complex->mirror, basename);
+      break;
+    default:
+      g_assert_not_reached();
+    }
+    g_free (basename);
   }
 
   return(buf);
@@ -815,36 +848,14 @@ char *o_complex_save(OBJECT *object)
  *  \par Function Description
  *
  */
-void o_complex_set_filename(TOPLEVEL *w_current, char *clib, char *basename) 
+void o_complex_set_filename(TOPLEVEL *w_current, const CLibSymbol *clib, char *basename) 
 {
-  int len;
-
-  if (basename == NULL) {
-    fprintf(stderr, "Got NULL basename in o_complex_set_filename!\n");
-    exit(-1);
-  }
-
   if (clib == NULL) {
     fprintf(stderr, "Got NULL clib in o_complex_set_filename!\n");
     exit(-1);
   }
 
-  if (w_current->internal_basename) {
-    g_free(w_current->internal_basename);
-  }
-
-  if (w_current->internal_clib) {
-    g_free(w_current->internal_clib);
-  }
-
-  len = strlen(basename);
-  w_current->internal_basename = (char *) g_malloc(sizeof(char)*len+1);
-
-  len = strlen(clib) + 1;	
-  w_current->internal_clib = (char *) g_malloc(sizeof(char)*len+1);
-
-  strcpy(w_current->internal_basename, basename);	
-  strcpy(w_current->internal_clib, clib);	
+  w_current->internal_clib = clib;
 } 
 
 /*! \brief
@@ -853,13 +864,7 @@ void o_complex_set_filename(TOPLEVEL *w_current, char *clib, char *basename)
  */
 void o_complex_free_filename(TOPLEVEL *w_current)
 {
-  if (w_current->internal_basename) {
-    g_free(w_current->internal_basename);
-  }
 
-  if (w_current->internal_clib) {
-    g_free(w_current->internal_clib);
-  }
 }
 
 /*! \brief
@@ -998,7 +1003,8 @@ OBJECT *o_complex_copy(TOPLEVEL *w_current, OBJECT *list_tail,
 
   new_obj = o_complex_add(w_current, list_tail, NULL, o_current->type, color,
                           o_current->complex->x, o_current->complex->y, 
-                          o_current->complex->angle, o_current->complex->mirror,
+                          o_current->complex->angle, 
+			  o_current->complex->mirror,
                           o_current->complex_clib, o_current->complex_basename, 
                           selectable, FALSE); 
 
@@ -1056,7 +1062,6 @@ OBJECT *o_complex_copy_embedded(TOPLEVEL *w_current, OBJECT *list_tail,
                                    color,
                                    o_current->complex->x, o_current->complex->y, 
                                    o_current->complex->angle, 
-                                   o_current->complex_clib, 
                                    o_current->complex_basename, 
                                    selectable); 
   /* deal with stuff that has changed */
diff --git a/libgeda/src/o_embed.c b/libgeda/src/o_embed.c
index dfe818a..b635900 100644
--- a/libgeda/src/o_embed.c
+++ b/libgeda/src/o_embed.c
@@ -49,27 +49,17 @@
  */
 void o_embed(TOPLEVEL *w_current, OBJECT *o_current)
 {
-  gchar *new_basename;
 
   /* check o_current is a complex and is not already embedded */
   if (o_current->type == OBJ_COMPLEX &&
       !o_complex_is_embedded (o_current))
   {
-    /* change the clib of complex to "EMBEDDED" */
-    if (o_current->complex_clib) {
-      g_free (o_current->complex_clib);
-    }
-    o_current->complex_clib = g_strdup ("EMBEDDED");
 
-    /* change the basename to "EMBEDDED"+basename */
-    new_basename = g_strconcat ("EMBEDDED",
-                                o_current->complex_basename,
-                                NULL);
-    g_free (o_current->complex_basename);
-    o_current->complex_basename = new_basename;
+    /* set the embedded flag */
+    o_current->complex_embedded = TRUE;
 
     s_log_message ("Component [%s] has been embedded\n",
-                   o_current->complex_basename + 8);
+                   o_current->complex_basename);
     
     /* page content has been modified */
     w_current->page_current->CHANGED = 1;
@@ -98,39 +88,34 @@ void o_embed(TOPLEVEL *w_current, OBJECT *o_current)
  */
 void o_unembed(TOPLEVEL *w_current, OBJECT *o_current)
 {
-  gchar *new_basename, *new_clib;
-  const GSList *clibs;
+  GList *symlist;
   
   /* check o_current is an embedded complex */
   if (o_current->type == OBJ_COMPLEX &&
       o_complex_is_embedded (o_current))
   {
-    /* get ride of the EMBEDDED word in basename */
-    new_basename = g_strdup (o_current->complex_basename + 8);
-    
+        
     /* search for the symbol in the library */
-    clibs = s_clib_search_basename (new_basename);
+    symlist = s_clib_glob (o_current->complex_basename);
 
-    if (!clibs) {
+    if (!symlist) {
       /* symbol not found in the symbol library: signal an error */
       s_log_message ("Could not find component [%s], while trying to unembed. Component is still embedded\n",
-                     o_current->complex_basename + 8);
+                     o_current->complex_basename);
       
     } else {
-      /* set the object new basename */
-      g_free (o_current->complex_basename);
-      o_current->complex_basename = new_basename;
 
       /* set the object new clib */
-      g_free (o_current->complex_clib);
-      if (g_slist_next (clibs)) {
+      if (g_list_next (symlist)) {
         s_log_message ("More than one component found with name [%s]\n",
-                       new_basename);
+                       o_current->complex_basename);
         /* PB: for now, use the first directory in clibs */
         /* PB: maybe open a dialog to select the right one? */
       }
-      new_clib = g_strdup ((gchar*)clibs->data);
-      o_current->complex_clib = new_clib;
+      o_current->complex_clib = (CLibSymbol *) symlist->data;
+
+      /* clear the embedded flag */
+      o_current->complex_embedded = FALSE;
 
       s_log_message ("Component [%s] has been successfully unembedded\n",
                      o_current->complex_basename);
diff --git a/libgeda/src/o_list.c b/libgeda/src/o_list.c
index 5c77f8d..11402b0 100644
--- a/libgeda/src/o_list.c
+++ b/libgeda/src/o_list.c
@@ -95,8 +95,7 @@ OBJECT *o_list_copy_to(TOPLEVEL *w_current, OBJECT *list_head,
 
     case(OBJ_COMPLEX):
     case(OBJ_PLACEHOLDER):
-      if (selected->complex_clib && 
-          strncmp(selected->complex_clib, "EMBEDDED", 8) == 0) {
+      if (o_complex_is_embedded (selected)) {
         end = (OBJECT *) o_complex_copy_embedded(w_current, end, selected);	
       } else {
         end = (OBJECT *) o_complex_copy(w_current, end, selected);	
diff --git a/libgeda/src/o_picture.c b/libgeda/src/o_picture.c
index e803ef5..91fa9d7 100644
--- a/libgeda/src/o_picture.c
+++ b/libgeda/src/o_picture.c
@@ -43,30 +43,32 @@
 
 /*! \brief Create picture OBJECT from character string.
  *  \par Function Description
- *  This function will get the description of a picture from the character
- *  string <B>*buf</B>. The new picture is then added to the list of object of
- *  which <B>*object_list</B> is the last element before the call.
- *  The function returns the new last element, that is the added
- *  picture object.
+ *  This function will get the description of a picture from the
+ *  character string <B>*first_line</B>. The new picture is then added
+ *  to the list of object of which <B>*object_list</B> is the last
+ *  element before the call.  The function returns the new last
+ *  element, that is the added picture object.
  *
  *  \param [in]  w_current       The TOPLEVEL object.
  *  \param [out] object_list     OBJECT list to create picture in.
- *  \param [in]  buf             Character string with picture description.
- *  \param [in]  fp              Picture file to read.
+ *  \param [in]  first_line      Character string with picture description.
+ *  \param [in]  tb              Text buffer to load embedded data from.
  *  \param [in]  release_ver     libgeda release version number.
  *  \param [in]  fileformat_ver  libgeda file format version number.
  *  \return A pointer to the new picture object.
  */
 OBJECT *o_picture_read(TOPLEVEL *w_current, OBJECT *object_list,
-		       char buf[], FILE *fp,
-		       unsigned int release_ver,unsigned int fileformat_ver)
+		       const char *first_line,
+		       TextBuffer *tb,
+		       unsigned int release_ver,
+		       unsigned int fileformat_ver)
 {
   int x1, y1;
   int width, height, angle;
   gchar mirrored, embedded;
   int num_conv;
   gchar type;
-  gchar buffer[MAX_TEXT_LINE_LENGTH]; 
+  gchar *line = NULL;
   gchar *filename;
   GdkPixbuf *pixbuf;
   static char gdk_initialized=0;
@@ -79,12 +81,12 @@ OBJECT *o_picture_read(TOPLEVEL *w_current, OBJECT *object_list,
     gdk_initialized = 1;
   }
 
-  num_conv = sscanf(buf, "%c %d %d %d %d %d %c %c\n",
+  num_conv = sscanf(first_line, "%c %d %d %d %d %d %c %c\n",
 	 &type, &x1, &y1, &width, &height, &angle, &mirrored, &embedded);
   
   if (num_conv != 8) {
-    fprintf(stderr, "Error reading picture definition line: %s.\n", buf);
-    s_log_message ("Error reading picture definition line: %s.\n", buf);
+    fprintf(stderr, "Error reading picture definition line: %s.\n", first_line);
+    s_log_message ("Error reading picture definition line: %s.\n", first_line);
   }
 
   /* Convert from ascii character to number */
@@ -137,11 +139,9 @@ OBJECT *o_picture_read(TOPLEVEL *w_current, OBJECT *object_list,
 
   }
 
-  fgets(buffer, 1024, fp);
-  
-  filename = g_strdup (buffer);
+  filename = g_strdup(s_textbuffer_next_line(tb));
   filename = remove_last_nl(filename);	
-    
+
   pixbuf = NULL;
 
   if (embedded == 0) {
@@ -160,10 +160,12 @@ OBJECT *o_picture_read(TOPLEVEL *w_current, OBJECT *object_list,
 
     /* Read the encoded picture */
     do {
-      finished = 0;
-      fgets(buffer, 1024, fp);
-      if (g_strcasecmp(buffer, ".\n") != 0) {
-	encoded_picture=g_string_append (encoded_picture, buffer);
+
+      line = s_textbuffer_next_line(tb);
+      if (line == NULL) break;
+
+      if (g_strcasecmp(line, ".\n") != 0) {
+	encoded_picture=g_string_append (encoded_picture, line);
 	encoded_picture=g_string_append (encoded_picture, "\n");
       }
       else {
diff --git a/libgeda/src/o_text_basic.c b/libgeda/src/o_text_basic.c
index 6b7dfc4..fd13a6d 100644
--- a/libgeda/src/o_text_basic.c
+++ b/libgeda/src/o_text_basic.c
@@ -1015,7 +1015,9 @@ void o_text_recalc(TOPLEVEL *w_current, OBJECT *o_current)
  *
  */
 OBJECT *o_text_read(TOPLEVEL *w_current, OBJECT *object_list,
-		    char buf[], FILE *fp, unsigned int release_ver,
+		    const char *first_line,
+		    TextBuffer *tb,
+		    unsigned int release_ver,
 		    unsigned int fileformat_ver)
 {
   char type; 
@@ -1032,21 +1034,21 @@ OBJECT *o_text_read(TOPLEVEL *w_current, OBJECT *object_list,
   GString *textstr;
 
   if (fileformat_ver == 1) {
-    sscanf(buf, "%c %d %d %d %d %d %d %d %d %d\n", &type, &x, &y, 
+    sscanf(first_line, "%c %d %d %d %d %d %d %d %d %d\n", &type, &x, &y, 
            &color, &size,
            &visibility, &show_name_value, 
            &angle, &alignment, &num_lines);	
   } else if (release_ver < VERSION_20000220) {
     /* yes, above less than (not less than and equal) is correct. The format */
     /* change occurred in 20000220 */
-    sscanf(buf, "%c %d %d %d %d %d %d %d\n", &type, &x, &y, 
+    sscanf(first_line, "%c %d %d %d %d %d %d %d\n", &type, &x, &y, 
            &color, &size,
            &visibility, &show_name_value, 
            &angle);	
     alignment = LOWER_LEFT; /* older versions didn't have this */
     num_lines = 1; /* only support a single line */
   } else {
-    sscanf(buf, "%c %d %d %d %d %d %d %d %d\n", &type, &x, &y, 
+    sscanf(first_line, "%c %d %d %d %d %d %d %d %d\n", &type, &x, &y, 
            &color, &size,
            &visibility, &show_name_value, 
            &angle, &alignment);	
@@ -1097,8 +1099,8 @@ OBJECT *o_text_read(TOPLEVEL *w_current, OBJECT *object_list,
   }
 
   if (color < 0 || color > MAX_COLORS) {
-    fprintf(stderr, "Found an invalid color [ %s ]\n", buf);
-    s_log_message("Found an invalid color [ %s ]\n", buf);
+    fprintf(stderr, "Found an invalid color [ %s ]\n", first_line);
+    s_log_message("Found an invalid color [ %s ]\n", first_line);
     s_log_message("Setting color to WHITE\n");
     color = WHITE;
   }
@@ -1107,11 +1109,14 @@ OBJECT *o_text_read(TOPLEVEL *w_current, OBJECT *object_list,
 
   textstr = g_string_new ("");
   for (i = 0; i < num_lines; i++) {
-    gchar buffer[MAX_TEXT_LINE_LENGTH];
+    gchar *line;
     
-    fgets (buffer, MAX_TEXT_LINE_LENGTH, fp);
+    line = s_textbuffer_next_line (tb);
 
-    textstr = g_string_append (textstr, buffer);
+    if (line != NULL)
+      {
+	textstr = g_string_append (textstr, line);
+      }
   }
   /* retrieve the character string from the GString */
   string = g_string_free (textstr, FALSE);
diff --git a/libgeda/src/s_basic.c b/libgeda/src/s_basic.c
index d60baba..0aa28d7 100644
--- a/libgeda/src/s_basic.c
+++ b/libgeda/src/s_basic.c
@@ -454,10 +454,6 @@ s_delete_object(TOPLEVEL *w_current, OBJECT *o_current)
     }
     o_current->complex_basename = NULL;
 
-    if (o_current->complex_clib) {
-      /*	printf("sdeleting complex_clib\n");*/
-      g_free(o_current->complex_clib); 
-    }
     o_current->complex_clib = NULL;
 
     if (o_current->complex) {
diff --git a/libgeda/src/s_clib.c b/libgeda/src/s_clib.c
index c1ec6cb..e5ea5c8 100644
--- a/libgeda/src/s_clib.c
+++ b/libgeda/src/s_clib.c
@@ -1,6 +1,6 @@
 /* gEDA - GPL Electronic Design Automation
  * libgeda - gEDA's library
- * Copyright (C) 1998-2000 Ales V. Hvezda
+ * Copyright (C) 1998-2007 gEDA Contributors
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -16,25 +16,102 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA
  */
+
 /*! \file s_clib.c
+ *  \brief The component library system
+ *
  *  <B>clib</B> stands for component library.
  *
- *  A component library is made of several directories gathering
- *  component files. 
+ *  The <b>component library</b> is made up of a number of
+ *  <b>component sources</b>, each of which in turn makes available a
+ *  number of component <b>symbols</b>.  Each source may be either a
+ *  directory on disk containing symbol files, or a command in the
+ *  system PATH which can generate gEDA symbol data (e.g. from a
+ *  database).  A component source is represented by a CLibSource
+ *  instance.
+ *
+ *  The component library system manages component sources and
+ *  symbols, and abstracts the interface to the underlying storage.
+ *
+ *  To initialise the component library, s_clib_init() is called.  To
+ *  clean up when it is no longer needed, s_clib_free() should be
+ *  called.
+ * 
+ *  A directory which contains one or more symbol files in gEDA
+ *  format may be used as a component source. Each symbol file should
+ *  have a filename ending in ".sym" (case sensitive).  A
+ *  component source based on a directory can be added using
+ *  s_clib_add_directory().  Symbol files with filenames starting with
+ *  a period "." are ignored.
+ *
+ *  An executable program in the system search path may be used as a
+ *  component source, and it must conform with the specification given
+ *  on page \ref libcmds.  A component source based on a command may
+ *  be added using s_clib_add_command().
+ *
+ *  Each symbol is identified by its \b name, which is stored in the
+ *  saved schematic file.  The name must be a valid for storage in a
+ *  gEDA schematic file as the "basename" of a "component" object.
+ *  For symbols from directory sources, the filename of the symbol is
+ *  taken as the symbol name.  For a command source, the name may be
+ *  any permissible string.  Guidelines to follow:
+ *
+ *    -# Do not begin a symbol name with "<tt>EMBEDDED</tt>"
+ *    -# Do not use whitespace, or any of the characters "<tt>/:!*?</tt>".
+ *    -# Try to use unique names.
+ *  
+ *  The component database may be queried using s_clib_glob().  A
+ *  null-terminated buffer containing symbol data (suitable for
+ *  loading using o_read_buffer()) may be obtained using
+ *  s_clib_symbol_get_data().  If an exact symbol name is known, the
+ *  symbol data may be requested directly using
+ *  s_clib_symbol_get_data_by_name().
+ *
+ *  \todo
+ *    -# Categorisation of symbols.
+ *
+ *    -# Case-insensitive matching of symbol file extensions (both ".sym"
+ *       and ".SYM" should match).
+ *
+ *  \page libcmds Library Commands
+ *
+ *  A library command should implement this specification.  Note that
+ *  as additional features may be added to the component library in
+ *  the future, ideally a library command should only respond to the
+ *  commands detailed here.
+ *
+ *  The command line syntax for a library command is:
+ *
+ *  <tt>libcmd \<mode\> [mode arguments]</tt>
+ *
+ *  All diagnostic and error information should be printed to standard
+ *  error.  Only data should be printed to standard output.  All data
+ *  output from a library command should be encoded using UTF8.
+ *
+ *  If an error occurs, the command must exit with non-zero exit
+ *  status, with any diagnostic information should be printed on
+ *  standard error.
+ *
+ *  \section libcmds_modes Modes
  *
- *  It must first be initialized with #s_clib_init(). When it is no more
- *  useful, use #s_clib_free() to free memory and reset the component
- *  library.
+ *  <tt>libcmd help</tt>
  *
- *  To add a directory to the library, use #s_clib_add_directory().
+ *  If \b help is passed as the mode, a command may output a help
+ *  message.  This mode is optional.
  *
- *  To retrieve a list of the directories that make the library, use
- *  #s_clib_get_directories(). For a list of component files in a
- *  directory of the library, use #s_clib_get_files().
+ *  <tt>libcmd list</tt>
  *
- *  #s_clib_search_basename() let you find a specific component from
- *  its name. Please note that it returns a list of directories as there
- *  may be several places that contains a component with this name.
+ *  If \b list is passed as the mode, a command must output a list of
+ *  the symbols it provides, separated by newlines.  Lines beginning
+ *  with a period '.' are ignored.  If an error occurs, the command
+ *  must exit with non-zero exit status.
+ *
+ *  <tt>libcmd get \<symbolname\>
+ *
+ *  If \b get is passed as the mode, a command must output the symbol
+ *  data corresponding to \b symbolname.  If \b symbolname is unknown
+ *  to the command, the command must exit with non-zero exit status.
+ *  
  */
 
 #include <config.h>
@@ -49,207 +126,860 @@
 #include <dmalloc.h>
 #endif
 
+#include <gtk/gtk.h>
+#include <libguile.h>
+
+#include <sys/wait.h>
+
 #include "defines.h"
+#include "struct.h"
+#include "globals.h"
+#include "o_types.h"
+#include "colors.h"
+#include "i_vars.h"
+#include "prototype.h"
+
+/* Constant definitions
+ * ===================
+ */
+
+/*! All symbols in directory sources end with this string */
+#define SYM_FILENAME_FILTER ".sym"
+
+/*! Library command mode used to fetch list of symbols */
+#define CLIB_LIST_CMD       "list"
 
+/*! Library command mode used to fetch symbol data */
+#define CLIB_DATA_CMD       "get"
 
-void s_clib_free (void);
+/* Type definitions
+ * ================
+ */
+
+/*! Valid types of component source */
+enum CLibSourceType { 
+  /*! Directory source */
+  CLIB_DIR, 
+  /*! Command source */
+  CLIB_CMD };
+
+/*! Stores data about a particular component source */
+struct _CLibSource {
+  /*! Type of source */
+  enum CLibSourceType type;
+  /*! Path to directory or name of executable */
+  gchar *path_cmd;
+  /*! Name of source */
+  gchar *name;
+  /*! Available symbols (CLibSymbol) */
+  GList *symbols;
+};
+
+/*! Stores data about a particular symbol */
+struct _CLibSymbol {
+  /*! The source this symbols is available from */
+  CLibSource *source;
+  /*! The name of this symbol */
+  gchar *name;
+};
 
-static GList *clib_directories = NULL;
+/* Static variables
+ * ================
+ */
+
+/*! Holds the list of all known component sources */
+static GList *clib_sources = NULL;
+
+/* Local static functions
+ * ======================
+ */
 
-static GHashTable *clib_cache = NULL;
+static void free_symbol (gpointer data, gpointer user_data);
+static void free_source (gpointer data, gpointer user_data);
+static gint compare_source_name (gconstpointer a, gconstpointer b);
+static gint compare_symbol_name (gconstpointer a, gconstpointer b);
+static gchar *run_source_command (gchar **argv);
+static CLibSymbol *source_has_symbol (const CLibSource *source, 
+				      const gchar *name);
+static void refresh_directory (CLibSource *source);
+static void refresh_command (CLibSource *source);
+static CLibSource *check_source_bump (const gchar *path_cmd);
+static gchar *get_data_directory (const CLibSymbol *symbol);
+static gchar *get_data_command (const CLibSymbol *symbol);
 
-/*! \brief Initializes the component library handling code.
+/*! \brief Initialise the component library.
  *  \par Function Description
- *  Initializes the component library handling code.
- *  \warning This function must be called before any other function
- *           of this file.
+ *  Resets and initialises the component library.
+ *
+ *  \warning This function must be called before any other functions
+ *  from s_clib.c.
  */
-void s_clib_init (void)
+void s_clib_init ()
 {
-  if (clib_directories != NULL || clib_cache != NULL) {
+  if (clib_sources != NULL) {
     s_clib_free ();
   }
-
-  clib_cache = g_hash_table_new (g_str_hash, g_str_equal);
-  
 }
 
-/*! \todo Finish function documentation!!!
- *  \brief
+/*! \brief Iterator callback for freeing a symbol.
  *  \par Function Description
- *
+ *  Private function used only in s_clib.c.
  */
-static void clib_free_cache_entry (gpointer key, gpointer value,
-				   gpointer user_data)
+static void free_symbol (gpointer data, gpointer user_data)
 {
-  g_free (key);
-  if (value != NULL) {
-    /* value is a singly-linked list of strings */
-    g_list_foreach (value, (GFunc)g_free, NULL);
-    g_slist_free ((GSList*)value);
+  CLibSymbol *symbol = data;
+  if (symbol != NULL) {
+    if (symbol->source != NULL) {
+      symbol->source = NULL;
+    }
+    if (symbol->name != NULL) {
+      g_free (symbol->name);
+      symbol->name = NULL;
+    }
   }
 }
 
-/*! \brief Frees memory used by the component library.
+/*! \brief Iterator callback for freeing a source.
  *  \par Function Description
- *  Frees memory used by the component library.
+ *  Private function used only in s_clib.c.
  */
-void s_clib_free (void)
+static void free_source (gpointer data, gpointer user_data)
 {
-  if (clib_directories != NULL) {
-    g_list_foreach (clib_directories, (GFunc)g_free, NULL);
-    g_list_free (clib_directories);
-    clib_directories = NULL;
+  CLibSource *source = data;
+  if (source != NULL) {
+    if (source->path_cmd != NULL) {
+      g_free (source->path_cmd);
+      source->path_cmd = NULL;
+    }
+    if (source->name != NULL) {
+      g_free (source->name);
+      source->name = NULL;
+    }
+    if (source->symbols != NULL) {
+      g_list_foreach (source->symbols, (GFunc) free_symbol, NULL);
+      g_list_free (source->symbols);
+      source->symbols = NULL;
+    }
   }
+}
 
-  if (clib_cache != NULL) {
-    g_hash_table_foreach (clib_cache, clib_free_cache_entry, NULL);
-    g_hash_table_destroy (clib_cache);
-    clib_cache = NULL;
+/*! \brief Free all memory used by the component library.
+ *  \par Function Description
+ *  Should be called at program exit to clean up any remaining data
+ *  being used by the component library system.
+ */
+void s_clib_free ()
+{
+  if (clib_sources != NULL) {
+    g_list_foreach (clib_sources, (GFunc) free_source, NULL);
+    g_list_free (clib_sources);
+    clib_sources = NULL;
   }
-  
 }
 
-/*! \brief Adds a new directory to the component library.
+/*! \brief Compare two component sources by name.
+ *  \par Function Description
+ *  Compare two component sources by name, case-insensitively.
+ *  Typically used when calling g_list_sort().  Private function used
+ *  only in s_clib.c.  Argument order is as strcasecmp().
+ *
+ *  \param a First source to compare
+ *  \param b Second source to compare
+ *
+ *  \return As strcasecmp().
+ */
+static gint compare_source_name (gconstpointer a, gconstpointer b)
+{
+  const CLibSource *src1 = a;
+  const CLibSource *src2 = b;
+
+  g_assert (src1 != NULL);
+  g_assert (src2 != NULL);
+
+  g_assert (src1->name != NULL);
+  g_assert (src2->name != NULL);
+
+  return strcasecmp(src1->name, src2->name);
+}
+
+/*! \brief Compare two component symbols by name.
+ *  \par Function Description
+ *  Compare two component symbols by name, case-insensitively.
+ *  Typically used when calling g_list_sort().  Private function used
+ *  only in s_clib.c.  Argument order is as strcasecmp().
+ *
+ *  \param a First symbol to compare
+ *  \param b Second symbol to compare
+ *
+ *  \return As strcasecmp().
+ */
+static gint compare_symbol_name (gconstpointer a, gconstpointer b)
+{
+  const CLibSymbol *sym1 = a;
+  const CLibSymbol *sym2 = b;
+
+  g_assert (sym1 != NULL);
+  g_assert (sym2 != NULL);
+
+  g_assert (sym1->name != NULL);
+  g_assert (sym2->name != NULL);
+
+  return strcasecmp(sym1->name, sym2->name);
+}
+
+
+/*! \brief Execute a library command.
  *  \par Function Description
- *  Adds <B>directory</B> as a new directory for the component library.
+ *  Execute a library command, returning the standard output, or \b
+ *  NULL if the command fails for some reason.  The system \b PATH is
+ *  used to find the program to execute.
+ *
+ *  Private function used only in s_clib.c.
  *
- *  \param [in] directory  Character string with the new directory name.
+ *  \param argv null-terminated list of arguments.  The name of the
+ *              program to execute should appear first.
+ *
+ *  \return The program's output, or \b NULL on failure.
  */
-void s_clib_add_directory (const gchar *directory)
+static gchar *run_source_command (gchar **argv)
 {
-  /* search for directory in clib_directories */
-  if (!g_list_find_custom (clib_directories,
-			   directory,
-			   (GCompareFunc) g_strcasecmp))
-  {
-    /* directory not yet in the list of known directories */
-    /* add directory to list */
-    clib_directories = g_list_append (clib_directories,
-				      g_strdup (directory));
+  gchar *standard_output = NULL;
+  gchar *standard_error = NULL;
+  gint exit_status;
+  GError *e = NULL;
+  gchar *command = NULL;
+  gboolean success = FALSE;
+  
+  g_spawn_sync (NULL, /* Use gschem's CWD */
+		argv,
+		NULL, /* No special environment */
+		G_SPAWN_SEARCH_PATH,
+		NULL, /* No setup function (not portable anyway) */
+		NULL, /* No user data */
+		&standard_output,
+		&standard_error,
+		&exit_status,
+		&e);
+
+  command = g_strjoinv (" ", argv);
+
+  if (e != NULL) {
+    s_log_message ("Library command failed [%s]: %s\n", command, 
+		   e->message);
+    g_error_free (e);
+
+  } else if (WIFSIGNALED(exit_status)) {
+    s_log_message ("Library command failed [%s]: Uncaught signal %i.\n",
+		   command, WTERMSIG(exit_status));
+    
+  } else if (!WIFEXITED(exit_status)) {
+    s_log_message ("Library command failed [%s]\n", command);
+    s_log_message("Error output was:\n%s\n", standard_error);
+
+  } else {
+    success = TRUE;
   }
+
+  g_free (command);
+  g_free (standard_error);
   
+  if (success) return standard_output;
+
+  g_free (standard_output);
+  return NULL;
 }
 
-/*! \brief Get list of component library directories.
+/*! \brief Get a list of available component sources.
  *  \par Function Description
- *  This function returns the list of directories part of
- *  the component library.
- *
- *  \return Global libgead #clib_directories variable.
- *  \warning
- *  The returned value is owned by libgeda and must not be modified or freed.
+ *  Gets the current list of sources.
+ *  \warning The GList returned should be freed when no longer
+ *  needed. The returned value is not guaranteed to remain valid over
+ *  calls to s_clib_add_directory() or s_clib_add_command().
+ *  \return A \b GList of CLibSource.
+ */
+GList *s_clib_get_sources ()
+{
+  GList *l = g_list_copy(clib_sources);
+  l = g_list_sort (l, (GCompareFunc) compare_source_name);
+  return l;
+}
+
+/*! \brief Find any symbols within a source with a given name.
+ *  \par Function Description
+ *  Iterates through the symbol list of the given source, checking if
+ *  there is already a symbol with the given name.  If there is
+ *  such a symbol, it is returned.
  *
+ *  \param source The source to check.
+ *  \param name The symbol name to look for.
+ *  \return The matching symbol, or \b NULL if no match was found.
  */
-const GList *s_clib_get_directories()
+static CLibSymbol *source_has_symbol (const CLibSource *source, 
+				      const gchar *name)
 {
-  return clib_directories;
+  GList *symlist;
+  CLibSymbol *symbol;
+
+  for (symlist = g_list_first(source->symbols); 
+       symlist != NULL; 
+       symlist = g_list_next(symlist)) {
+    
+    symbol = (CLibSymbol *) symlist->data;
+
+    if (strcmp (symbol->name, name) == 0) return symbol;
+  }
+
+  return NULL;
 }
 
-/*! \brief Get a list of files found a directory.
+/*! \brief Rescan a directory for symbols.
  *  \par Function Description
- *  This function returns a list of file names found in <B>directory</B> and
- *  that match <B>filter</B>
+ *  Rescans a directory for symbols.
  *
- *  \param [in] directory  Character string with the path to search.
- *  \param [in] filter     Character string to compare file names against.
- *  \return List of file name that matched <B>filter</B>, NULL otherwise.
+ *  \todo Does this need to do something more sane with subdirectories
+ *  than just skipping them silently?
+ *
+ *  Private function used only in s_clib.c.
  */
-GSList *s_clib_get_files (const gchar *directory, const gchar *filter)
+static void refresh_directory (CLibSource *source)
 {
+  CLibSymbol *symbol;
   GDir *dir;
   const gchar *entry;
-  GSList *ret = NULL;
-
-  /* check directory is in clib_directories */
-  if (g_list_find_custom (clib_directories,
-			  directory,
-			  (GCompareFunc) g_strcasecmp) == NULL)
-  {
-    /* no, unknown directory: report an error */
-    s_log_message ("Directory [%s] is not part of the component library\n",
-                   directory);
-    return NULL;
-  }
+  gchar *fullpath;
+  gboolean isfile;
+  GError *e = NULL;
 
-  /* open the directory */
-  dir = g_dir_open (directory, 0, NULL);
-  if (dir == NULL) {
-    s_log_message ("Failed to open directory [%s]\n", directory);
-    return NULL;
+  g_assert (source != NULL);
+  g_assert (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->path_cmd, 0, &e);
+
+  if (e != NULL) {
+    s_log_message ("Failed to open directory [%s]: %s\n",
+		   source->path_cmd, e->message);
+    g_error_free (e);
+    return;
   }
 
-  /* now read the entire directory */
-  /* and build a list of filenames in directory that match filter */
   while ((entry = g_dir_read_name (dir)) != NULL) {
-    /* skip .'s */
-    if (entry[0] == '.') {
+    /* skip ".", ".." & hidden files */
+    if (entry[0] == '.') continue;
+
+    /* skip subdirectories (for now) */
+    fullpath = g_build_filename (source->path_cmd, entry, NULL);
+    isfile = g_file_test (fullpath, G_FILE_TEST_IS_REGULAR);
+    g_free (fullpath);
+    if (!isfile) continue;
+
+    /* skip filenames that don't match the filter or that we already
+     * know about. */
+    if (!g_str_has_suffix (entry, SYM_FILENAME_FILTER)
+	|| (source_has_symbol (source, entry) != NULL)) {
       continue;
     }
 
-    /* identify filter-matching filenames */
-    if (strstr (entry, filter)) {
-      ret = g_slist_append (ret, (gpointer)g_strdup (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);
+}
+
+/*! \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;
+  gchar *argv[3];
+
+  g_assert (source != NULL);
+  g_assert (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 */
+  argv[0] = source->path_cmd;
+  argv[1] = CLIB_LIST_CMD;
+  argv[2] = NULL;
+  cmdout = run_source_command ( argv );
+  if (cmdout == NULL) return;
+
+  /* Use a TextBuffer to help reading out the lines of the output */
+  tb = s_textbuffer_new (cmdout, -1);
+
+  while (1) {
+    line = s_textbuffer_next_line (tb);
+    if (line == NULL) break;
+    if (line[0] == '.') continue;  /* TODO is this sane? */
+
+    name = remove_nl(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);
+}
+
+/*! \brief Rescan all available component libraries.
+ *  \par Function Description
+ *  Resets the list of symbols available from each source, and
+ *  repopulates it from scratch.  Useful e.g. for checking for new
+ *  symbols.
+ *
+ *  \todo Disabled for now because it would break cached CLibSymbols used
+ *  all over the place (e.g. in #st_object).
+ */
+void s_clib_refresh ()
+{
+#if 0
+  GList *sourcelist;
+  CLibSource *source;
+
+  for (sourcelist = clib_sources; 
+       sourcelist != NULL; 
+       sourcelist = g_list_next(sourcelist)) {
+    
+    source = (CLibSource *) sourcelist->data;
+    switch (source->type)
+      {
+      case CLIB_DIR:
+	refresh_directory(source);
+	break;
+      case CLIB_CMD:
+	refresh_command (source);
+	break;
+      default:
+	g_assert_not_reached();
+      }
+  }
+#endif
+}
+
+/*! \brief Check if a given component source is already known.
+ *  \par Function Description
+ *  Iterates through the known component sources, checking if there is
+ *  already a source associated with \b path_cmd.  If there is such a
+ *  source, it is bumped to the front of the list of component sources.
+ *  
+ *  Private function used only in s_clib.c.
+ *
+ *  \param path_cmd The source string to check.
+ *
+ *  \return The matching source, or \b NULL if no match was found.
+ */
+static CLibSource *check_source_bump (const gchar *path_cmd)
+{
+  GList *sourcelist;
+  CLibSource *source;
+
+  for (sourcelist = clib_sources; 
+       sourcelist != NULL; 
+       sourcelist = g_list_next(sourcelist)) {
+
+    source = (CLibSource *) sourcelist->data;
+    if (strcmp (source->path_cmd, path_cmd) == 0) {
+      clib_sources = g_list_remove_link (clib_sources, sourcelist);
+      clib_sources = g_list_concat (sourcelist, clib_sources);
+      return source;
+    }
+  }
+
+  return NULL;
+}
+
+/*! \brief Add a directory of symbol files to the library
+ *  \par Function Description
+ *  Adds a directory containing symbol files to the library.  Only
+ *  files ending with #SYM_FILENAME_FILTER are considered to be symbol
+ *  files.  A \a name may be specified for the source; if \a name is
+ *  \b NULL, the basename of the directory as returned by
+ *  g_path_get_basename() is used.
+ *
+ *  \param directory The path of the directory to add (UTF8).
+ *  \param name      A descriptive name for the directory.
+ *  \return The #CLibSource associated with the directory.
+ */
+const CLibSource *s_clib_add_directory (const gchar *directory, 
+					const gchar *name)
+{
+  CLibSource *source;
+
+  if (directory == NULL) {
+    return NULL;
   }
   
-  /* finished: close the directory stream */
-  g_dir_close (dir);
+  source = check_source_bump (directory);
+  if (source != NULL) return source;
+
+  source = g_new0 (CLibSource, 1);
+  source->type = CLIB_DIR;
+  source->path_cmd = g_strdup (directory);
+  
+  if (name == NULL) {
+    source->name = g_path_get_basename (directory);
+  } else {
+    source->name = g_strdup(name);
+  }
 
-  /* sort the list alphabetically */
-  ret = g_slist_sort (ret, (GCompareFunc)g_strcasecmp);
+  refresh_directory (source);
+
+  /* Sources added later get scanned earlier */
+  clib_sources = g_list_prepend (clib_sources, source);
+
+  return source;
+}
+
+/*! \brief Add a symbol-generating command to the library
+ *  \par Function Description
+ *  Adds a command which can generate symbols to the library.  See
+ *  page \ref libcmds for more information on library commands.  A \a
+ *  name may be specified for the source; if \a name is \b NULL, the
+ *  command is used as the name.
+ *  
+ *  \param command The executable to run, resolved using the \b PATH
+ *                 environment variable.
+ *  \param name    A descriptive name for the command.
+ *  \return The CLibSource associated with the command.
+ */
+const CLibSource *s_clib_add_command (const gchar *command,
+				      const gchar *name)
+{
+  CLibSource *source;
   
-  /* and return the sorted list of filenames */
-  return ret;
+  if (command == NULL) {
+    return NULL;
+  }
+
+  source = check_source_bump (command);
+  if (source != NULL) return source;
+
+  source = g_new0 (CLibSource, 1);
+  source->type = CLIB_CMD;
+  source->path_cmd = g_strdup (command);
+
+  if (name == NULL) {
+    source->name = g_strdup (command);
+  } else {
+    source->name = g_strdup (name);
+  }
+
+  refresh_command (source);
+
+  /* Sources added later get scanned earlier */
+  clib_sources = g_list_prepend (clib_sources, source);
+
+  return source;
+}
+
+
+/*! \brief Get the name of a source.
+ *  \par Function Description
+ *  Get the name of a source for use e.g. in displaying a GUI.
+ *
+ *  \todo Make this do something cleverer than just returning
+ *  \b path_cmd.
+ *
+ *  \param source Source to be examined.
+ *  \return Name of source.
+*/
+const gchar *s_clib_source_get_name (const CLibSource *source)
+{
+  if (source == NULL) return NULL;
+  return source->name;
+}
+
+/*! \brief Get a list of symbols available from a given source.
+ *  \par Function Description
+ *  Get a \b GList containing all of the symbols available from \a
+ *  source.
+ *
+ *  \warning The returned \b GList will not be consistent over a call to
+ *  s_clib_refresh().  It should be freed when no longer needed.
+ *  
+ *  \param source Source to be examined.
+ *  \return A \b GList of #CLibSymbol.
+ */
+GList *s_clib_source_get_symbols (const CLibSource *source)
+{
+  if (source == NULL) return NULL;
+  return g_list_copy(source->symbols);
+}
+
+
+/*! \brief Get the name of a symbol.
+ *  \par Function Description
+ *  Get the name of a symbol.  The symbol name uniquely identifies it
+ *  to libgeda.
+ *
+ *  \param symbol Symbol to be examined.
+ *  \return Name of symbol.
+*/
+const gchar *s_clib_symbol_get_name (const CLibSymbol *symbol)
+{
+  if (symbol == NULL) return NULL;
+  return symbol->name;
 }
 
-/*! \brief Search for a symbol file in the component library.
+/*! \brief Get a filename for editing a symbol.  
  *  \par Function Description
- *  Searches in component library for a symbol file with name <B>basename</B>.
+ *  Get the filename of the file a symbol was loaded from, if possible
+ *  (e.g. to allow loading for user editing).
+ *
+ *  \warning The returned string should be freed when no longer
+ *  needed.
  *
- *  \param [in] basename  Character string with base symbol name to search for.
- *  \return List of directories where symbol file with this name was found,
- *          NULL otherwise.
+ *  \todo This is hack until there is a way to edit documents in
+ *  gschem which do not have a file in the filesystem associated with
+ *  them.
  *
- *  \warning
- *  The returned value is owned by libgeda and must not be modified or freed.
+ *  \deprecated This function is a temporary workaround.
  *
+ *  \param symbol Symbol to be examined.
+ *  \return Filename of symbol.
  */
-const GSList *s_clib_search_basename(const gchar *basename)
+gchar *s_clib_symbol_get_filename (const CLibSymbol *symbol)
 {
-  GSList *ret; 
-  GList *tmp;
+  if (symbol == NULL) return NULL;
+
+  if (symbol->source->type != CLIB_DIR) return NULL;
+
+  return g_build_filename(symbol->source->path_cmd, symbol->name, NULL);
+}
+
+/*! \brief Get the source to which a symbol belongs.
+ *  \par Function Description
+ *  Get the source which a symbol is associated.
+ *
+ *  \param symbol Symbol to be examined.
+ *  \return Source which owns symbol.
+*/
+const CLibSource *s_clib_symbol_get_source (const CLibSymbol *symbol)
+{
+  if (symbol == NULL) return NULL;
+  return symbol->source;
+}
+
+/*! \brief Get symbol data from a directory source.
+ *  \par Function Description
+ *  Get symbol data from a directory data source.  The return value
+ *  should be free()'d when no longer needed.
+ *
+ *  Private function used only in s_clib.c.
+ *
+ *  \param symbol Symbol to get data for.
+ *  \return Allocated buffer containing symbol data.
+ */
+static gchar *get_data_directory (const CLibSymbol *symbol)
+{
+  gchar *filename = NULL;
+  gchar *data = NULL;
+  GError *e = NULL;
+
+  g_assert (symbol != NULL);
+  g_assert (symbol->source->type == CLIB_DIR);
+
+  filename = g_build_filename(symbol->source->path_cmd, 
+			      symbol->name, NULL);
+
+  g_file_get_contents (filename, &data, NULL, &e);
+
+  if (e != NULL) {
+    s_log_message ("Failed to load symbol from file [%s]: %s\n",
+		   filename, e->message);
+    g_error_free (e);
+  }
+
+  g_free (filename);
+  return data;
+}
+
+/*! \brief Get symbol data from a library command.
+ *  \par Function Description
+ *  Get symbol data from a library command.  The return value should
+ *  be free()'d when no longer needed.
+ *
+ *  Private function used only in s_clib.c.
+ *
+ *  \param symbol Symbol to get data for.
+ *  \return Allocated buffer containing symbol data.
+ */
+static gchar *get_data_command (const CLibSymbol *symbol)
+{
+  gchar *argv[4];
+
+  g_assert (symbol != NULL);
+  g_assert (symbol->source->type == CLIB_CMD);
   
-  /* first check if basename is in cache */
-  ret = g_hash_table_lookup (clib_cache, basename);
-  if (ret != NULL) {
-    /* yes, found basename in cache, nothing more to do */
-    return ret;
-  }
-
-  /* looks like we have to search for basename in the library */
-  for (tmp = g_list_last(clib_directories); 
-       tmp != NULL; tmp = g_list_previous (tmp)) {
-    gchar *dir_name  = (gchar*)tmp->data;
-    gchar *file_name = g_strconcat (dir_name,
-                                    G_DIR_SEPARATOR_S,
-                                    basename,
-                                    NULL);
-
-    if (g_file_test (file_name, G_FILE_TEST_EXISTS)) {
-      /* add directory name to the list */
-      ret = g_slist_append (ret, g_strdup (dir_name));
+  argv[0] = symbol->source->path_cmd;
+  argv[1] = CLIB_DATA_CMD;
+  argv[2] = symbol->name;
+  argv[3] = NULL;
+
+  return run_source_command ( argv );
+}
+
+/*! \brief Get symbol data.
+ *  \par Function Description
+ *  Get the unparsed gEDA-format data corresponding to a symbol from
+ *  the symbol's data source.  The return value should be free()'d
+ *  when no longer needed.
+ *
+ *  On failure, returns \b NULL (the error will be logged).
+ *
+ *  \param symbol Symbol to get data for.
+ *  \return Allocated buffer containing symbol data.
+ */
+gchar *s_clib_symbol_get_data (const CLibSymbol *symbol)
+{
+  g_assert (symbol != NULL);
+
+  switch (symbol->source->type)
+    {
+    case CLIB_DIR:
+      return get_data_directory (symbol);
+    case CLIB_CMD:
+      return get_data_command (symbol);
+    default:
+      g_assert_not_reached();
     }
+}
+
+
+/*! \brief Find all symbols matching a glob pattern.  \par Function
+ *  Description Searches the library, returning all symbols whose
+ *  names match \a glob (see the GLib documentation for details of the
+ *  glob syntax applicable).
+ *
+ *  \warning The #CLibSymbol instances in the \b GList returned belong
+ *  to the component library, and should be considered constants; they
+ *  should not be manipulated or free()'d.  On the other hand, the \b
+ *  GList returned must be freed with \b g_list_free() when no longer
+ *  needed.  Note that the values returned will be invalidated by a
+ *  call to s_clib_free() or s_clib_refresh().
+ *
+ *  \param glob The glob pattern to match against.
+ *  \return A \b GList of matching #CLibSymbol structures.
+ */
+GList *s_clib_glob (const gchar *glob)
+{  
+  GList *sourcelist;
+  GList *symlist;
+  GList *result = NULL;
+  CLibSource *source;
+  CLibSymbol *symbol;
+  GPatternSpec *pattern;
+
+  if (glob == NULL) return NULL;
 
-    g_free (file_name);
+  pattern = g_pattern_spec_new(glob);
+
+  for (sourcelist = clib_sources; 
+       sourcelist != NULL; 
+       sourcelist = g_list_next(sourcelist)) {
+
+    source = (CLibSource *) sourcelist->data;
+
+    for (symlist = source->symbols;
+	 symlist != NULL;
+	 symlist = g_list_next(symlist)) {
+    
+      symbol = (CLibSymbol *) symlist->data;
+
+      if (g_pattern_match_string (pattern, symbol->name)) {
+	result = g_list_prepend (result, symbol);
+      }
+
+    }
+    
   }
 
-  /* have we found something? */
-  if (ret != NULL) {
-    /* yes, add the result to cache */
-    g_hash_table_insert (clib_cache, g_strdup (basename), ret);
+  result = g_list_reverse (result);
+
+  g_pattern_spec_free (pattern);
+
+  return result;
+}
+
+/*! \brief Get symbol data for a given symbol name.
+ *  \par Function Description
+ *  Return the data for the first symbol found with the given name.
+ *  This is a helper function for the schematic load system, as it
+ *  will always want to load symbols given only their name.
+ *
+ *  On failure, returns \b NULL (the error will be logged).
+ *
+ *  \todo Speed this up repeated calls by caching the #CLibSymbol
+ *  pointers found for each name requested.
+ *
+ *  \param name The symbol name to match against.
+ *  \return Allocated buffer containing symbol data.
+ */
+gchar *s_clib_symbol_get_data_by_name (const gchar *name)
+{
+  GList *sourcelist;
+  CLibSource *source;
+  CLibSymbol *symbol;
+
+  for (sourcelist = clib_sources; 
+       sourcelist != NULL; 
+       sourcelist = g_list_next(sourcelist)) {
+
+    source = (CLibSource *) sourcelist->data;
+
+    symbol = source_has_symbol (source, name);
+
+    if (symbol != NULL) {
+      return s_clib_symbol_get_data (symbol);
+    }
+    
   }
-  
-  return ret;
+
+  return NULL;
 }
diff --git a/libgeda/src/s_hierarchy.c b/libgeda/src/s_hierarchy.c
index 179d8ee..46f97f1 100644
--- a/libgeda/src/s_hierarchy.c
+++ b/libgeda/src/s_hierarchy.c
@@ -214,17 +214,23 @@ void s_hierarchy_down_schematic_multiple (TOPLEVEL *w_current,
  *
  */
 void s_hierarchy_down_symbol (TOPLEVEL *w_current,
-			      const gchar *filename, PAGE *parent)
+			      const CLibSymbol *symbol, PAGE *parent)
 {
   PAGE *page;
+  gchar *filename;
+
+  filename = s_clib_symbol_get_filename (symbol);
 
   page = s_page_search (w_current, filename);
   if (page) {
     s_page_goto (w_current, page);
+    g_free (filename);
     return;
   }
 
   page = s_page_new (w_current, filename);
+  g_free(filename);
+
   s_page_goto (w_current, page);
 
   f_open(w_current, page->page_filename);
diff --git a/libgeda/src/s_textbuffer.c b/libgeda/src/s_textbuffer.c
new file mode 100644
index 0000000..7b05953
--- /dev/null
+++ b/libgeda/src/s_textbuffer.c
@@ -0,0 +1,218 @@
+/* gEDA - GPL Electronic Design Automation
+ * libgeda - gEDA's library
+ * Copyright (C) 1998-2000 Ales V. Hvezda
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA
+ */
+
+#include <config.h>
+
+#include <glib.h>
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#ifdef HAVE_LIBDMALLOC
+#include <dmalloc.h>
+#endif
+
+#include <gtk/gtk.h>
+#include <libguile.h>
+
+#include "defines.h"
+#include "struct.h"
+#include "globals.h"
+#include "o_types.h"
+#include "colors.h"
+#include "i_vars.h"
+#include "prototype.h"
+
+struct _TextBuffer
+{
+  gchar *buffer;
+  gsize size;
+
+  gchar *line;
+  gsize linesize;
+
+  gsize offset;
+};
+
+#define TEXT_BUFFER_LINE_SIZE 1024
+
+/*! \brief Create a new managed text buffer.
+ *
+ *  \par Function description 
+ *  Allocates and initialises a new TextBuffer to manage the given data
+ *  buffer.
+ *
+ *  If the size argument is negative, assumes that data is
+ *  null-terminated.
+ *
+ *  \param data The address of the buffer to be managed.
+ *  \param size The length of the buffer.
+ *  \retval Pointer to a new TextBuffer struct.
+ */
+TextBuffer *s_textbuffer_new (gchar *data, const gint size)
+{
+  TextBuffer *result;
+  gsize realsize;
+
+  g_assert (data != NULL);
+
+  if (size < 0)
+    realsize = strlen(data);
+  else
+    realsize = size;
+
+  result = g_new0(TextBuffer, 1);
+
+  result->buffer = data;
+  result->size = realsize;
+
+  result->linesize = TEXT_BUFFER_LINE_SIZE;
+  result->line = g_malloc(result->linesize);
+
+  return result;
+}
+
+/*! \brief Clean up a managed text buffer
+ *
+ *  \par Function description
+ *  Cleans up all of the resources associated with a given TextBuffer.
+ *
+ *  Should be called thus:
+ *
+ *  \code
+ *  tb = s_textbuffer_free (tb);
+ *  \endcode
+ */
+TextBuffer *s_textbuffer_free (TextBuffer *tb)
+{
+  if (tb == NULL) return NULL;
+
+  g_free (tb->line);
+  tb->line = NULL;
+  g_free (tb);
+  return NULL;
+}
+
+/*! \brief Change the current position within a text buffer
+ *
+ *  \par Function description
+ *  Changes where the next call to s_textbuffer_next() will start
+ *  reading.  If offset is negative, it is considered as a distance
+ *  from the end of the buffer.
+ *
+ *  \param tb     A TextBuffer to seek within.
+ *  \param offset A new position within the buffer.
+ */
+void s_textbuffer_seek (TextBuffer *tb, const gint offset)
+{
+  gint ofs;
+  gsize realoffset;
+
+  if (tb == NULL) return;
+
+  ofs = offset;
+  if (ofs > tb->size)
+    ofs = tb->size;
+
+  if (ofs < -tb->size)
+    ofs = 0;
+
+  if (ofs < 0)
+    realoffset = tb->size - ofs;
+  else
+    realoffset = ofs;
+
+  tb->offset = realoffset;
+}
+
+/*! \brief Fetch a number of characters from a text buffer
+ *
+ *  \par Function description
+ *  Get some number of characters from a TextBuffer, starting at the
+ *  current position.  If the end of the buffer has been reached (and
+ *  thus no more characters remain) returns null.
+ *
+ *  The returned character array should be considered highly volatile,
+ *  and is only valid until the next call to s_textbuffer_next() or
+ *  s_textbuffer_next_line().
+ *
+ *  \param tb    TextBuffer to read from.
+ *  \param count Maximum number of characters to read.
+ *  \retval      Character array, or NULL if no characters left.
+ */
+gchar *s_textbuffer_next (TextBuffer *tb, const gsize count)
+{
+  gsize len = count;
+  gsize maxlen = tb->size - tb->offset;
+
+  if (tb == NULL) return NULL;
+
+  if (count == 0) return NULL;
+
+  if (tb->offset >= tb->size) 
+    return NULL;
+
+  if (len > maxlen) 
+    len = maxlen;
+
+  if (tb->linesize < len + 1) {
+    tb->line = g_realloc(tb->line, len + 1);
+    tb->linesize = len + 1;
+  }
+
+  strncpy (tb->line, tb->buffer + tb->offset, len);
+
+  tb->line[len] = 0;
+  tb->offset += len;
+
+  return tb->line;
+}
+/*! \brief Fetch the next line from a text buffer
+ *
+ *  \par Function description
+ *  Get the next line of characters from a TextBuffer, starting from
+ *  the current position.  If the end of the buffer has been reached
+ *  (and thus no more characters remain) returns null.
+ *
+ *  The returned character array should be considered highly volatile,
+ *  and is only valid until the next call to s_textbuffer_next() or
+ *  s_textbuffer_next_line().
+ *
+ *  \param tb    TextBuffer to read from.
+ *  \retval      Character array, or NULL if no characters left.
+ */
+gchar *s_textbuffer_next_line (TextBuffer *tb)
+{
+  int len = 0;
+
+  if (tb == NULL) return NULL;
+
+  if (tb->offset >= tb->size) 
+    return NULL;
+
+  while ((tb->buffer[tb->offset + len] != '\n')
+	 && (len < tb->size - tb->offset - 1)) {
+    len++;
+  }
+
+  len++;
+
+  return s_textbuffer_next (tb, len);
+}
diff --git a/libgeda/src/s_toplevel.c b/libgeda/src/s_toplevel.c
index dafe32f..0628945 100644
--- a/libgeda/src/s_toplevel.c
+++ b/libgeda/src/s_toplevel.c
@@ -93,7 +93,6 @@ TOPLEVEL *s_toplevel_new (void)
   toplevel->current_visible = -1; /* not sure on these */
   toplevel->current_show    = -1;
 
-  toplevel->internal_basename = NULL;
   toplevel->internal_clib = NULL;
   
   toplevel->RC_list = NULL;

PGP signature



_______________________________________________
geda-dev mailing list
geda-dev@moria.seul.org
http://www.seul.org/cgi-bin/mailman/listinfo/geda-dev