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

gEDA-dev: [RFC][PATCH] Making libgeda load from buffers



Hi folks,

In my quest for integrated support of part databases into gschem, I realised 
that libgeda has no way to load schematics or symbols from a memory buffer: 
all of the object read commands require the presence of a file pointer.

Ideally, I would have liked to implement reading from memory using memory 
streams, but then I realised they were a GNU extension to libc.

What I've done in the attached patches is the following: I've added a new text 
buffer system (s_textbuffer.c) that basically lets you iterate over the lines 
of a string in memory, doing appropriate bounds checking.  I've then modified 
all of the commands used when parsing schematics/symbols to use this API.

Now, I'm sure there's a **much** more elegant way to do this, and I'd very 
much like someone to point it out.  The changes are pretty well contained 
within the read functions, so as long as I get to keep my o_read_buffer() 
function, I don't really mind how it's implemented.

Peter


-- 
Fisher Society committee                    http://tinyurl.com/o39w2
CUSBC novices, match and league secretary   http://tinyurl.com/mwrc9
CU Spaceflight                              http://tinyurl.com/ognu2

      09f911029d74e35bd84156c5635688c0            peter-b.co.uk

PGP signature

Add managed text buffers to libgeda.

From: Peter TB Brett <peter@peter-b.co.uk>

In order to facilitate loading symbols & schematics from
memory without an extensive rewrite, this patch adds an API
for reading memory arrays a bit like a file.

Using memory streams & fgets would be nicer, but unfortunately
memory streams are a GNU libc extension.
---

 libgeda/include/prototype.h |    6 +
 libgeda/include/struct.h    |    5 +
 libgeda/src/Makefile.am     |    2 
 libgeda/src/s_textbuffer.c  |  192 +++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 204 insertions(+), 1 deletions(-)

diff --git a/libgeda/include/prototype.h b/libgeda/include/prototype.h
index 0aacafc..b105588 100644
--- a/libgeda/include/prototype.h
+++ b/libgeda/include/prototype.h
@@ -635,3 +635,9 @@ 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);
+const gchar *s_textbuffer_next (TextBuffer *tb, const gsize count);
diff --git a/libgeda/include/struct.h b/libgeda/include/struct.h
index 8b4e9c8..47b9062 100644
--- a/libgeda/include/struct.h
+++ b/libgeda/include/struct.h
@@ -952,4 +952,9 @@ struct st_page_smob {
   PAGE   *page;
 };
 
+/* Managed text buffers
+ * ==================== */
+
+typedef struct _TextBuffer TextBuffer;
+
 #endif
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/s_textbuffer.c b/libgeda/src/s_textbuffer.c
new file mode 100644
index 0000000..2472143
--- /dev/null
+++ b/libgeda/src/s_textbuffer.c
@@ -0,0 +1,192 @@
+/* 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
+ *
+ *
+ *  \param 
+ */
+const gchar *s_textbuffer_next (TextBuffer *tb, const gsize count)
+{
+  gsize len = count;
+  gsize maxlen = tb->size - tb->offset;
+
+  if (tb == NULL) 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 + offset, len);
+  tb->line[len] = 0;
+  tb->offset += len;
+
+  return tb->line;
+}
+
+const 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)) {
+    len++;
+  }
+
+  return s_textbuffer_next (tb, len);
+}
Make libgeda parse schematics from buffered data.

From: Peter TB Brett <peter@peter-b.co.uk>

In order to support more complex methods of acquiring symbol
data, libgeda needs to be able to load schematics and symbols
directly from a memory buffer as well as from files.
---

 libgeda/include/prototype.h |   25 +++++++--
 libgeda/src/a_basic.c       |  120 +++++++++++++++++++++++++++++--------------
 libgeda/src/o_attrib.c      |   44 +++++++++-------
 libgeda/src/o_picture.c     |   36 +++++++------
 libgeda/src/o_text_basic.c  |   23 +++++---
 libgeda/src/s_basic.c       |   89 ++++++++++++++++++++++++++++++++
 libgeda/src/s_textbuffer.c  |   13 +++--
 7 files changed, 259 insertions(+), 91 deletions(-)

diff --git a/libgeda/include/prototype.h b/libgeda/include/prototype.h
index b105588..405af9b 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 size_t 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);
 
@@ -162,7 +163,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);
@@ -370,8 +375,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 +434,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,6 +485,7 @@ 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 */
@@ -640,4 +654,5 @@ int u_basic_count_char(const char *string, char character);
 TextBuffer *s_textbuffer_new (gchar *data, const gint size);
 TextBuffer *s_textbuffer_free (TextBuffer *tb);
 void s_textbuffer_seek (TextBuffer *tb, const gint offset);
-const gchar *s_textbuffer_next (TextBuffer *tb, const gsize count);
+gchar *s_textbuffer_next (TextBuffer *tb, const gsize count);
+gchar *s_textbuffer_next_line (TextBuffer *tb);
diff --git a/libgeda/src/a_basic.c b/libgeda/src/a_basic.c
index 1b0e8e5..5e633de 100644
--- a/libgeda/src/a_basic.c
+++ b/libgeda/src/a_basic.c
@@ -288,19 +288,13 @@ int o_save(TOPLEVEL *w_current, const char *filename)
   return 1;
 }
 
-/*! \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)
+OBJECT *o_read_buffer(TOPLEVEL *w_current, OBJECT *object_list, 
+		      char *buffer, const size_t 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 +309,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);
 
-    sscanf(buf, "%c", &objtype);
+  while (1) {
+
+    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 +345,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 +392,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 +414,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 +455,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 +475,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 +485,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 +493,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 +506,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 +532,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/o_attrib.c b/libgeda/src/o_attrib.c
index 49cb79c..0ef513f 100644
--- a/libgeda/src/o_attrib.c
+++ b/libgeda/src/o_attrib.c
@@ -647,37 +647,44 @@ 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 buffer.
  *
  *  \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]  buffer                 Data buffer to read from.
+ *  \param [in]  buffer_size            Size of data buffer.
+ *  \param [in,out] offset       Location of offset of start of attrib block.
  *  \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 +692,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 +714,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 +724,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 +737,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_picture.c b/libgeda/src/o_picture.c
index e803ef5..aa956d7 100644
--- a/libgeda/src/o_picture.c
+++ b/libgeda/src/o_picture.c
@@ -51,22 +51,26 @@
  *
  *  \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]  buffer          Buffer containing picture data.
+ *  \param [in]  buffer_size     Length of data buffer.
+ *  \param [in,out] offset       Location of offset of start of next line.
  *  \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 +83,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 +141,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 +162,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 cd5eff2..85a26d7 100644
--- a/libgeda/src/o_text_basic.c
+++ b/libgeda/src/o_text_basic.c
@@ -1013,7 +1013,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; 
@@ -1030,21 +1032,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);	
@@ -1095,8 +1097,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;
   }
@@ -1105,11 +1107,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..6cef76e 100644
--- a/libgeda/src/s_basic.c
+++ b/libgeda/src/s_basic.c
@@ -845,6 +845,95 @@ char *insert_string(char *string, int start, char *insert_string)
   return(new_string);
 }
 
+/*!
+ *  \brief Get the next line of a text buffer.
+ *
+ *  \par Function Description
+ *
+ *  Retrieves the next line of a text data buffer into a
+ *  newly-allocated string of the correct size.  The size of the
+ *  buffer is specified with the size argument, which may be set to
+ *  zero or negative if the buffer is null-terminated, in which case
+ *  the size argument is ignored. If offset is not NULL, it should be
+ *  the location at which buffer_next_line() may store the index of
+ *  the character immediately following the last character in the line
+ *  (i.e. the line length).  If the value of offset is non-zero, it is
+ *  taken as the index of the first character of the line.
+ *
+ *  If offset is beyond the end of the buffer, or the character at
+ *  offset is null, returns a null pointer.
+ *
+ *  An example of repeatedly obtaining lines:
+ *
+ *  \code
+ *  char *buffer, *line;
+ *  size_t buffersize
+ *  size_t offset = 0;
+ *
+ *  // ... initialise buffer ...
+ *
+ *  while (line = buffer_next_line (buffer, buffersize, &offset)) {
+ *    printf (line);
+ *    free (line);
+ *  }
+ *  \endcode
+ *
+ *  \param[in]     buffer  Buffer to read from
+ *  \param[in]     size    Total size of buffer 
+ *  \param[in,out] offset  Location of offset of beginning of line
+ *
+ *  \retval  Pointer to newly-allocated string buffer containing line
+ */
+char *buffer_next_line(const char *buffer, const size_t size, size_t *offset)
+{
+  size_t n = 0;
+  size_t ofs = 0;
+  size_t len;
+  char *result;
+
+  g_assert (buffer != NULL);
+
+  /* Use the current value of linelen as the starting offset */
+  if (offset != NULL) ofs = *offset;
+
+  n = ofs;
+
+  if ((size > 0) && (n >= size)) return NULL;
+  if (buffer[n] == 0) return NULL;
+
+  while (1) {
+    /* Check if we've reached the end of the buffer */
+    if ((size > 0) && (n >= size)) {
+      n--;
+      break;
+    }
+    /* Check if we've reached a null */
+    if (buffer[n] == 0) {
+      n--; /* Exclude trailing null */
+      break;
+    }
+    /* Check if we've reached the end of a line */
+    if (buffer[n] == '\n') {
+      break;
+    }
+    n++;
+  }
+
+  /* n now contains the *offset* of the last character we want to
+   * copy, so now we add one and subract the initial offset in order
+   * to find the length to copy. */
+  if (size > 0) g_assert (n < size);
+  len = n + 1 - ofs;
+  
+  result = g_strndup (buffer + *offset, len);
+  if (offset != NULL)
+    {
+      *offset += len;/* offset now contains index of first char of next line */
+    }
+  return result;
+}
+
+
 /*! \todo Finish function documentation!!!
  *  \brief
  *  \par Function Description
diff --git a/libgeda/src/s_textbuffer.c b/libgeda/src/s_textbuffer.c
index 2472143..496b6fb 100644
--- a/libgeda/src/s_textbuffer.c
+++ b/libgeda/src/s_textbuffer.c
@@ -149,13 +149,15 @@ void s_textbuffer_seek (TextBuffer *tb, const gint offset)
  *
  *  \param 
  */
-const gchar *s_textbuffer_next (TextBuffer *tb, const gsize count)
+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;
 
@@ -167,14 +169,15 @@ const gchar *s_textbuffer_next (TextBuffer *tb, const gsize count)
     tb->linesize = len + 1;
   }
 
-  strncpy (tb->line, tb->buffer + offset, len);
+  strncpy (tb->line, tb->buffer + tb->offset, len);
+
   tb->line[len] = 0;
   tb->offset += len;
 
   return tb->line;
 }
 
-const gchar *s_textbuffer_next_line (TextBuffer *tb)
+gchar *s_textbuffer_next_line (TextBuffer *tb)
 {
   int len = 0;
 
@@ -184,9 +187,11 @@ const gchar *s_textbuffer_next_line (TextBuffer *tb)
     return NULL;
 
   while ((tb->buffer[tb->offset + len] != '\n')
-	 && (len < tb->size - tb->offset)) {
+	 && (len < tb->size - tb->offset - 1)) {
     len++;
   }
 
+  len++;
+
   return s_textbuffer_next (tb, len);
 }


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