? alternative.patch
? flexfile.patch
Index: src/data/automake.mk
===================================================================
RCS file: /cvsroot/pspp/pspp/src/data/automake.mk,v
retrieving revision 1.6
diff -u -p -r1.6 automake.mk
--- src/data/automake.mk	5 May 2006 04:53:12 -0000	1.6
+++ src/data/automake.mk	13 Jul 2006 05:27:03 -0000
@@ -1,4 +1,3 @@
-## Process this file with automake to produce Makefile.in  -*- makefile -*-
 
 noinst_LIBRARIES += src/data/libdata.a
 
@@ -14,8 +13,13 @@ src_data_libdata_a_SOURCES = \
 	src/data/case-source.c \
 	src/data/case-source.h \
 	src/data/case.c \
-	src/data/casefile.c \
 	src/data/casefile.h \
+	src/data/casefile.c \
+	src/data/casefile-private.h \
+	src/data/fastfile.c \
+	src/data/fastfile.h \
+	src/data/flexifile.c \
+	src/data/flexifile.h \
 	src/data/case.h \
 	src/data/category.c \
 	src/data/category.h \
Index: src/data/casefile-private.h
===================================================================
RCS file: src/data/casefile-private.h
diff -N src/data/casefile-private.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ src/data/casefile-private.h	13 Jul 2006 05:27:03 -0000
@@ -0,0 +1,103 @@
+/* PSPP - computes sample statistics.
+   Copyright (C) 2004, 2006 Free Software Foundation, Inc.
+   Written by Ben Pfaff <blp@gnu.org>.
+
+   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., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301, USA. */
+
+#ifndef CASEFILE_PRIVATE_H
+#define CASEFILE_PRIVATE_H
+
+#include <config.h>
+#include <stdbool.h>
+#include <libpspp/ll.h>
+
+struct ccase;
+struct casereader;
+struct casefile;
+
+struct class_casefile
+{
+  void (*destroy) (struct casefile *) ;
+
+  bool (*error) (const struct casefile *) ;
+
+  size_t (*get_value_cnt) (const struct casefile *) ;
+  unsigned long (*get_case_cnt) (const struct casefile *) ;
+
+  struct casereader * (*get_reader) (const struct casefile *) ; 
+
+  struct casereader * (*get_destructive_reader) (struct casefile *) ;
+
+  bool (*append) (struct casefile *, const struct ccase *) ;
+  bool (*append_xfer) (struct casefile *, struct ccase *) ;
+
+  bool (*sleep) (const struct casefile *) ;
+  bool (*in_core) (const struct casefile *) ;
+  bool (*to_disk) (const struct casefile *) ;
+};
+
+struct casefile
+{
+  const struct class_casefile *class ;   /* Class pointer */
+
+  struct ll_list reader_list ;       /* List of our readers. */
+  struct ll ll ;                 /* Element in the class' list of casefiles. */
+};
+
+
+struct class_casereader
+{
+  bool (*read) (struct casereader *, struct ccase *);
+  bool (*read_xfer) (struct casereader *, struct ccase *);
+
+  unsigned long (*cnum) (const struct casereader *);
+
+  void (*destroy) (struct casereader * r);
+};
+
+
+#define CLASS_CASEREADER(K) ( (struct class_casereader *) K)
+
+struct casereader
+{
+  const struct class_casereader *class;  /* Class pointer */
+
+  struct casefile *cf;   /* The casefile to which this reader belongs */
+  struct ll ll;          /* Element in the casefile's list of readers */
+};
+
+
+#define CASEFILE(C)        (casefile_cast ( (struct casefile *) C))
+#define CONST_CASEFILE(C) (const_casefile_cast ( (const struct casefile *) C))
+
+#define CASEFILEREADER(CR) (casereader_cast ((struct casereader *) CR))
+
+/* Casts */
+struct casefile * casefile_cast(struct casefile *cf);
+const struct casefile * const_casefile_cast(const struct casefile *cf);
+
+struct casereader * casereader_cast(struct casereader *cr);
+
+/* Functions for implementations' use  only */
+
+void casefile_register (struct casefile *cf, 
+			const struct class_casefile *k);
+
+void casereader_register (struct casefile *cf, 
+			  struct casereader *reader, 
+			  const struct class_casereader *k);
+
+#endif
Index: src/data/casefile.c
===================================================================
RCS file: /cvsroot/pspp/pspp/src/data/casefile.c,v
retrieving revision 1.14
diff -u -p -r1.14 casefile.c
--- src/data/casefile.c	13 Jul 2006 03:57:34 -0000	1.14
+++ src/data/casefile.c	13 Jul 2006 05:27:03 -0000
@@ -1,5 +1,5 @@
 /* PSPP - computes sample statistics.
-   Copyright (C) 2004 Free Software Foundation, Inc.
+   Copyright (C) 2004, 2006 Free Software Foundation, Inc.
    Written by Ben Pfaff <blp@gnu.org>.
 
    This program is free software; you can redistribute it and/or
@@ -18,545 +18,181 @@
    02110-1301, USA. */
 
 #include <config.h>
-#include "casefile.h"
-#include <assert.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <stdio.h>
+#include <stddef.h>
+#include <stdbool.h>
 #include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <libpspp/alloc.h>
+#include <assert.h>
+
 #include "case.h"
-#include <libpspp/compiler.h>
-#include <libpspp/message.h>
-#include "full-read.h"
-#include "full-write.h"
-#include <libpspp/misc.h>
-#include <libpspp/str.h>
-#include "make-file.h"
-#include "settings.h"
-#include "variable.h"
-
-#include "gettext.h"
-#define _(msgid) gettext (msgid)
-
-#define IO_BUF_SIZE (8192 / sizeof (union value))
-
-/* A casefile represents a sequentially accessible stream of
-   immutable cases.
-
-   If workspace allows, a casefile is maintained in memory.  If
-   workspace overflows, then the casefile is pushed to disk.  In
-   either case the interface presented to callers is kept the
-   same.
-
-   The life cycle of a casefile consists of up to three phases:
-
-       1. Writing.  The casefile initially contains no cases.  In
-          this phase, any number of cases may be appended to the
-          end of a casefile.  (Cases are never inserted in the
-          middle or before the beginning of a casefile.)
-
-          Use casefile_append() or casefile_append_xfer() to
-          append a case to a casefile.
-
-       2. Reading.  The casefile may be read sequentially,
-          starting from the beginning, by "casereaders".  Any
-          number of casereaders may be created, at any time,
-          during the reading phase.  Each casereader has an
-          independent position in the casefile.
-
-          Ordinary casereaders may only move forward.  They
-          cannot move backward to arbitrary records or seek
-          randomly.  Cloning casereaders is possible, but it is
-          not yet implemented.
-
-          Use casefile_get_reader() to create a casereader for
-          use in phase 2.  This also transitions from phase 1 to
-          phase 2.  Calling casefile_mode_reader() makes the same
-          transition, without creating a casereader.
-
-          Use casereader_read() or casereader_read_xfer() to read
-          a case from a casereader.  Use casereader_destroy() to
-          discard a casereader when it is no longer needed.
-
-          "Random" casereaders, which support a seek operation,
-          may also be created.  These should not, generally, be
-          used for statistical procedures, because random access
-          is much slower than sequential access.  They are
-          intended for use by the GUI.
-
-       3. Destruction.  This phase is optional.  The casefile is
-          also read with casereaders in this phase, but the
-          ability to create new casereaders is curtailed.
-
-          In this phase, casereaders could still be cloned (once
-          we eventually implement cloning).
-
-          To transition from phase 1 or 2 to phase 3 and create a
-          casereader, call casefile_get_destructive_reader().
-          The same functions apply to the casereader obtained
-          this way as apply to casereaders obtained in phase 2.
-          
-          After casefile_get_destructive_reader() is called, no
-          more casereaders may be created with
-          casefile_get_reader() or
-          casefile_get_destructive_reader().  (If cloning of
-          casereaders were implemented, it would still be
-          possible.)
-
-          The purpose of the limitations applied to casereaders
-          in phase 3 is to allow in-memory casefiles to fully
-          transfer ownership of cases to the casereaders,
-          avoiding the need for extra copies of case data.  For
-          relatively static data sets with many variables, I
-          suspect (without evidence) that this may be a big
-          performance boost.
+#include "casefile.h"
+#include "casefile-private.h"
 
-   When a casefile is no longer needed, it may be destroyed with
-   casefile_destroy().  This function will also destroy any
-   remaining casereaders. */
 
-/* FIXME: should we implement compression? */
+struct ccase;
 
-/* In-memory cases are arranged in an array of arrays.  The top
-   level is variable size and the size of each bottom level array
-   is fixed at the number of cases defined here.  */
-#define CASES_PER_BLOCK 128             
-
-/* A casefile. */
-struct casefile 
-  {
-    /* Basic data. */
-    struct casefile *next, *prev;       /* Next, prev in global list. */
-    size_t value_cnt;                   /* Case size in `union value's. */
-    size_t case_acct_size;              /* Case size for accounting. */
-    unsigned long case_cnt;             /* Number of cases stored. */
-    enum { MEMORY, DISK } storage;      /* Where cases are stored. */
-    enum { WRITE, READ } mode;          /* Is writing or reading allowed? */
-    struct casereader *readers;         /* List of our readers. */
-    bool being_destroyed;               /* Does a destructive reader exist? */
-    bool ok;                            /* False after I/O error. */
-
-    /* Memory storage. */
-    struct ccase **cases;               /* Pointer to array of cases. */
-
-    /* Disk storage. */
-    int fd;                             /* File descriptor, -1 if none. */
-    char *file_name;                    /* File name. */
-    union value *buffer;                /* I/O buffer, NULL if none. */
-    size_t buffer_used;                 /* Number of values used in buffer. */
-    size_t buffer_size;                 /* Buffer size in values. */
-  };
-
-/* For reading out the cases in a casefile. */
-struct casereader 
-  {
-    struct casereader *next, *prev;     /* Next, prev in casefile's list. */
-    struct casefile *cf;                /* Our casefile. */
-    unsigned long case_idx;             /* Case number of current case. */
-    bool destructive;                   /* Is this a destructive reader? */
-    bool random;                        /* Is this a random reader? */
-
-    /* Disk storage. */
-    int fd;                             /* File descriptor. */
-    off_t file_ofs;                     /* Current position in fd. */
-    off_t buffer_ofs;                   /* File offset of buffer start. */
-    union value *buffer;                /* I/O buffer. */
-    size_t buffer_pos;                  /* Offset of buffer position. */
-    struct ccase c;                     /* Current case. */
-  };
+/* A casefile is an abstract class representing an array of cases.  In
+   general, cases are accessible sequentially,  and are immutable once
+   appended to the casefile.  However some implementations may provide
+   special methods for  case mutation or random access.
 
-/* Return the case number of the current case */
-unsigned long
-casereader_cnum(const struct casereader *r)
-{
-  return r->case_idx;
-}
+   Use casefile_append or casefile_append_xfer to append a case to a
+   casefile. 
 
-/* Doubly linked list of all casefiles. */
-static struct casefile *casefiles;
+   The casefile may be read sequentially,
+   starting from the beginning, by "casereaders".  Any
+   number of casereaders may be created, at any time.
+   Each casereader has an independent position in the casefile.
 
-/* Number of bytes of case allocated in in-memory casefiles. */
-static size_t case_bytes;
+   Casereaders may only move forward.  They cannot move backward to
+   arbitrary records or seek randomly.  Cloning casereaders is
+   possible, but it is not yet implemented.
 
-static void register_atexit (void);
-static void exit_handler (void);
+   Use casereader_read() or casereader_read_xfer() to read
+   a case from a casereader.  Use casereader_destroy() to
+   discard a casereader when it is no longer needed.
 
-static void reader_open_file (struct casereader *);
-static void write_case_to_disk (struct casefile *, const struct ccase *);
-static void flush_buffer (struct casefile *);
-static void seek_and_fill_buffer (struct casereader *);
-static bool fill_buffer (struct casereader *);
+   When a casefile is no longer needed, it may be destroyed with
+   casefile_destroy().  This function will also destroy any
+   remaining casereaders. */
 
-static void io_error (struct casefile *, const char *, ...)
-     PRINTF_FORMAT (2, 3);
-static int safe_open (const char *file_name, int flags);
-static int safe_close (int fd);
+static struct ll_list all_casefiles = LL_INITIALIZER (all_casefiles);
 
-/* Creates and returns a casefile to store cases of VALUE_CNT
-   `union value's each. */
-struct casefile *
-casefile_create (size_t value_cnt) 
+static struct casefile *
+ll_to_casefile (const struct ll *ll)
 {
-  struct casefile *cf = xmalloc (sizeof *cf);
-  cf->next = casefiles;
-  cf->prev = NULL;
-  if (cf->next != NULL)
-    cf->next->prev = cf;
-  casefiles = cf;
-  cf->value_cnt = value_cnt;
-  cf->case_acct_size = (cf->value_cnt + 4) * sizeof *cf->buffer;
-  cf->case_cnt = 0;
-  cf->storage = MEMORY;
-  cf->mode = WRITE;
-  cf->readers = NULL;
-  cf->being_destroyed = 0;
-  cf->ok = true;
-  cf->cases = NULL;
-  cf->fd = -1;
-  cf->file_name = NULL;
-  cf->buffer = NULL;
-  cf->buffer_size = ROUND_UP (cf->value_cnt, IO_BUF_SIZE);
-  if (cf->value_cnt > 0 && cf->buffer_size % cf->value_cnt > 64)
-    cf->buffer_size = cf->value_cnt;
-  cf->buffer_used = 0;
-  register_atexit ();
-  return cf;
+  return ll_data (ll, struct casefile, ll);
 }
 
-/* Destroys casefile CF. */
-void
-casefile_destroy (struct casefile *cf) 
+static struct casereader *
+ll_to_casereader (const struct ll *ll)
 {
-  if (cf != NULL) 
-    {
-      if (cf->next != NULL)
-        cf->next->prev = cf->prev;
-      if (cf->prev != NULL)
-        cf->prev->next = cf->next;
-      if (casefiles == cf)
-        casefiles = cf->next;
-
-      while (cf->readers != NULL) 
-        casereader_destroy (cf->readers);
-
-      if (cf->cases != NULL) 
-        {
-          size_t idx, block_cnt;
-
-          case_bytes -= cf->case_cnt * cf->case_acct_size;
-          for (idx = 0; idx < cf->case_cnt; idx++)
-            {
-              size_t block_idx = idx / CASES_PER_BLOCK;
-              size_t case_idx = idx % CASES_PER_BLOCK;
-              struct ccase *c = &cf->cases[block_idx][case_idx];
-              case_destroy (c);
-            }
-
-          block_cnt = DIV_RND_UP (cf->case_cnt, CASES_PER_BLOCK);
-          for (idx = 0; idx < block_cnt; idx++)
-            free (cf->cases[idx]);
-
-          free (cf->cases);
-        }
-
-      if (cf->fd != -1)
-        safe_close (cf->fd);
-          
-      if (cf->file_name != NULL && remove (cf->file_name) == -1) 
-        io_error (cf, _("%s: Removing temporary file: %s."),
-                  cf->file_name, strerror (errno));
-      free (cf->file_name);
-
-      free (cf->buffer);
-
-      free (cf);
-    }
+  return ll_data (ll, struct casereader, ll);
 }
 
-/* Returns true if an I/O error has occurred in casefile CF. */
-bool
-casefile_error (const struct casefile *cf) 
-{
-  return !cf->ok;
-}
 
-/* Returns true only if casefile CF is stored in memory (instead of on
-   disk), false otherwise. */
-bool
-casefile_in_core (const struct casefile *cf) 
+/* atexit() handler that closes and deletes our temporary
+   files. */
+static void
+exit_handler (void) 
 {
-  assert (cf != NULL);
-
-  return cf->storage == MEMORY;
+  while (!ll_is_empty (&all_casefiles))
+    casefile_destroy (ll_to_casefile (ll_head (&all_casefiles)));
 }
 
-/* Puts a casefile to "sleep", that is, minimizes the resources
-   needed for it by closing its file descriptor and freeing its
-   buffer.  This is useful if we need so many casefiles that we
-   might not have enough memory and file descriptors to go
-   around.
-
-   For simplicity, this implementation always converts the
-   casefile to reader mode.  If this turns out to be a problem,
-   with a little extra work we could also support sleeping
-   writers.
-
-   Returns true if successful, false if an I/O error occurred. */
-bool
-casefile_sleep (const struct casefile *cf_) 
+/* Insert CF into the global list of casefiles */
+void
+casefile_register (struct casefile *cf, const struct class_casefile *class)
 {
-  struct casefile *cf = (struct casefile *) cf_;
-  assert (cf != NULL);
-
-  casefile_mode_reader (cf);
-  casefile_to_disk (cf);
-  flush_buffer (cf);
-
-  if (cf->fd != -1) 
-    {
-      safe_close (cf->fd);
-      cf->fd = -1;
-    }
-  if (cf->buffer != NULL) 
+  static bool initialised ;
+  if ( !initialised ) 
     {
-      free (cf->buffer);
-      cf->buffer = NULL;
+      atexit (exit_handler);
+      initialised = true;
     }
 
-  return cf->ok;
+  cf->class = class;
+  ll_push_head (&all_casefiles, &cf->ll);
+  ll_init (&cf->reader_list);
 }
 
-/* Returns the number of `union value's in a case for CF. */
-size_t
-casefile_get_value_cnt (const struct casefile *cf) 
+/* Remove CF from the global list */
+static void
+casefile_unregister(struct casefile *cf)
 {
-  assert (cf != NULL);
-
-  return cf->value_cnt;
+  ll_remove (&cf->ll);
 }
 
-/* Returns the number of cases in casefile CF. */
-unsigned long
-casefile_get_case_cnt (const struct casefile *cf) 
+/* Return the casefile corresponding to this reader */
+struct casefile *
+casereader_get_casefile (const struct casereader *r)
 {
-  assert (cf != NULL);
-
-  return cf->case_cnt;
+  return r->cf;
 }
 
-/* Appends a copy of case C to casefile CF.  Not valid after any
-   reader for CF has been created.
-   Returns true if successful, false if an I/O error occurred. */
-bool
-casefile_append (struct casefile *cf, const struct ccase *c) 
+/* Return the case number of the current case */
+unsigned long
+casereader_cnum(const struct casereader *r)
 {
-  assert (cf != NULL);
-  assert (c != NULL);
-  assert (cf->mode == WRITE);
-
-  assert ( cf->value_cnt <= c->case_data->value_cnt );
-
-  /* Try memory first. */
-  if (cf->storage == MEMORY) 
-    {
-      if (case_bytes < get_workspace ())
-        {
-          size_t block_idx = cf->case_cnt / CASES_PER_BLOCK;
-          size_t case_idx = cf->case_cnt % CASES_PER_BLOCK;
-          struct ccase new_case;
-
-          case_bytes += cf->case_acct_size;
-          case_clone (&new_case, c);
-          if (case_idx == 0) 
-            {
-              if ((block_idx & (block_idx - 1)) == 0) 
-                {
-                  size_t block_cap = block_idx == 0 ? 1 : block_idx * 2;
-                  cf->cases = xnrealloc (cf->cases,
-                                         block_cap, sizeof *cf->cases);
-                }
-
-              cf->cases[block_idx] = xnmalloc (CASES_PER_BLOCK,
-                                               sizeof **cf->cases);
-            }
-
-          case_move (&cf->cases[block_idx][case_idx], &new_case);
-        }
-      else
-        {
-          casefile_to_disk (cf);
-          assert (cf->storage == DISK);
-          write_case_to_disk (cf, c);
-        }
-    }
-  else
-    write_case_to_disk (cf, c);
-
-  cf->case_cnt++;
-  return cf->ok;
+  return r->class->cnum(r);
 }
 
-/* Appends case C to casefile CF, which takes over ownership of
-   C.  Not valid after any reader for CF has been created.
-   Returns true if successful, false if an I/O error occurred. */
+/* Reads a copy of the next case from READER into C.
+   Caller is responsible for destroying C.
+   Returns true if successful, false at end of file. */
 bool
-casefile_append_xfer (struct casefile *cf, struct ccase *c) 
+casereader_read (struct casereader *r, struct ccase *c)
 {
-  casefile_append (cf, c);
-  case_destroy (c);
-  return cf->ok;
+  return r->class->read(r, c);
 }
 
-/* Writes case C to casefile CF's disk buffer, first flushing the buffer to
-   disk if it would otherwise overflow.
-   Returns true if successful, false if an I/O error occurred. */
-static void
-write_case_to_disk (struct casefile *cf, const struct ccase *c) 
+/* Reads the next case from READER into C and transfers ownership
+   to the caller.  Caller is responsible for destroying C.
+   Returns true if successful, false at end of file or on I/O
+   error. */
+bool 
+casereader_read_xfer (struct casereader *r, struct ccase *c)
 {
-  if (!cf->ok)
-    return;
-  
-  case_to_values (c, cf->buffer + cf->buffer_used, cf->value_cnt);
-  cf->buffer_used += cf->value_cnt;
-  if (cf->buffer_used + cf->value_cnt > cf->buffer_size)
-    flush_buffer (cf);
+  return r->class->read_xfer(r, c);
 }
 
-/* If any bytes in CF's output buffer are used, flush them to
-   disk. */
-static void
-flush_buffer (struct casefile *cf) 
+
+/* Destroys R. */
+void 
+casereader_destroy (struct casereader *r)
 {
-  if (cf->ok && cf->buffer_used > 0) 
-    {
-      if (!full_write (cf->fd, cf->buffer,
-                       cf->buffer_size * sizeof *cf->buffer))
-        io_error (cf, _("Error writing temporary file: %s."),
-                  strerror (errno));
-      cf->buffer_used = 0;
-    }
+  ll_remove (&r->ll);
+
+  r->class->destroy(r);
 }
 
-/* If CF is currently stored in memory, writes it to disk.  Readers, if any,
-   retain their current positions.
-   Returns true if successful, false if an I/O error occurred. */
-bool
-casefile_to_disk (const struct casefile *cf_) 
+/* Destroys casefile CF. */
+void
+casefile_destroy(struct casefile *cf)
 {
-  struct casefile *cf = (struct casefile *) cf_;
-  struct casereader *reader;
+  if (!cf) return;
   
-  assert (cf != NULL);
+  assert(cf->class->destroy);
 
-  if (cf->storage == MEMORY)
-    {
-      size_t idx, block_cnt;
+  while (!ll_is_empty (&cf->reader_list))
+    casereader_destroy (ll_to_casereader (ll_head (&cf->reader_list)));
       
-      assert (cf->file_name == NULL);
-      assert (cf->fd == -1);
-      assert (cf->buffer_used == 0);
-
-      if (!make_temp_file (&cf->fd, &cf->file_name))
-        {
-          cf->ok = false;
-          return false;
-        }
-      cf->storage = DISK;
-
-      cf->buffer = xnmalloc (cf->buffer_size, sizeof *cf->buffer);
-      memset (cf->buffer, 0, cf->buffer_size * sizeof *cf->buffer);
-
-      case_bytes -= cf->case_cnt * cf->case_acct_size;
-      for (idx = 0; idx < cf->case_cnt; idx++)
-        {
-          size_t block_idx = idx / CASES_PER_BLOCK;
-          size_t case_idx = idx % CASES_PER_BLOCK;
-          struct ccase *c = &cf->cases[block_idx][case_idx];
-          write_case_to_disk (cf, c);
-          case_destroy (c);
-        }
-
-      block_cnt = DIV_RND_UP (cf->case_cnt, CASES_PER_BLOCK);
-      for (idx = 0; idx < block_cnt; idx++)
-        free (cf->cases[idx]);
+  casefile_unregister(cf);
 
-      free (cf->cases);
-      cf->cases = NULL;
+  cf->class->destroy(cf);
+}
 
-      if (cf->mode == READ)
-        flush_buffer (cf);
+/* Returns true if an I/O error has occurred in casefile CF. */
+bool 
+casefile_error (const struct casefile *cf)
+{
+  return cf->class->error(cf);
+}
 
-      for (reader = cf->readers; reader != NULL; reader = reader->next)
-        reader_open_file (reader);
-    }
-  return cf->ok;
+/* Returns the number of cases in casefile CF. */
+unsigned long 
+casefile_get_case_cnt (const struct casefile *cf)
+{
+  return cf->class->get_case_cnt(cf);
 }
 
-/* Changes CF to reader mode, ensuring that no more cases may be
-   added.  Creating a casereader for CF has the same effect. */
-void
-casefile_mode_reader (struct casefile *cf) 
+/* Returns the number of `union value's in a case for CF. */
+size_t 
+casefile_get_value_cnt (const struct casefile *cf)
 {
-  assert (cf != NULL);
-  cf->mode = READ;
+  return cf->class->get_value_cnt(cf);
 }
 
 /* Creates and returns a casereader for CF.  A casereader can be used to
    sequentially read the cases in a casefile. */
 struct casereader *
-casefile_get_reader (const struct casefile *cf_) 
-{
-  struct casefile *cf = (struct casefile *) cf_;
-  struct casereader *reader;
-
-  assert (cf != NULL);
-  assert (!cf->being_destroyed);
-
-  /* Flush the buffer to disk if it's not empty. */
-  if (cf->mode == WRITE && cf->storage == DISK)
-    flush_buffer (cf);
-  
-  cf->mode = READ;
-
-  reader = xmalloc (sizeof *reader);
-  reader->next = cf->readers;
-  if (cf->readers != NULL)
-    reader->next->prev = reader;
-  cf->readers = reader;
-  reader->prev = NULL;
-  reader->cf = cf;
-  reader->case_idx = 0;
-  reader->destructive = 0;
-  reader->random = false;
-  reader->fd = -1;
-  reader->buffer = NULL;
-  reader->buffer_pos = 0;
-  case_nullify (&reader->c);
-
-  if (reader->cf->storage == DISK) 
-    reader_open_file (reader);
-
-  return reader;
-}
-
-/* Creates and returns a random casereader for CF.  A random
-   casereader can be used to randomly read the cases in a
-   casefile. */
-struct casereader *
-casefile_get_random_reader (const struct casefile *cf) 
+casefile_get_reader (const struct casefile *cf)
 {
-  struct casefile  *mutable_casefile = (struct casefile*) cf;
-  struct casereader *reader;
+  struct casereader *r = cf->class->get_reader(cf);
+  r->cf = (struct casefile *) cf;
 
-  enum { WRITE, READ } mode = cf->mode ;
-  reader = casefile_get_reader (cf);
-  reader->random = true;
-  mutable_casefile->mode = mode;
+  assert (r->class);
   
-  return reader;
+  return r;
 }
 
 /* Creates and returns a destructive casereader for CF.  Like a
@@ -568,303 +204,92 @@ casefile_get_random_reader (const struct
 struct casereader *
 casefile_get_destructive_reader (struct casefile *cf) 
 {
-  struct casereader *reader;
-  
-  assert (cf->readers == NULL);
-  reader = casefile_get_reader (cf);
-  reader->destructive = 1;
-  cf->being_destroyed = 1;
-  return reader;
+  struct casereader *r = cf->class->get_destructive_reader(cf);
+  r->cf = cf;
+
+  return r;
 }
 
-/* Opens a disk file for READER and seeks to the current position as indicated
-   by case_idx.  Normally the current position is the beginning of the file,
-   but casefile_to_disk may cause the file to be opened at a different
-   position. */
-static void
-reader_open_file (struct casereader *reader) 
+/* Appends a copy of case C to casefile CF. 
+   Returns true if successful, false if an I/O error occurred. */
+bool 
+casefile_append (struct casefile *cf, const struct ccase *c)
 {
-  struct casefile *cf = reader->cf;
-  if (!cf->ok || reader->case_idx >= cf->case_cnt)
-    return;
-
-  if (cf->fd != -1) 
-    {
-      reader->fd = cf->fd;
-      cf->fd = -1;
-    }
-  else 
-    {
-      reader->fd = safe_open (cf->file_name, O_RDONLY);
-      if (reader->fd < 0)
-        io_error (cf, _("%s: Opening temporary file: %s."),
-                  cf->file_name, strerror (errno));
-    }
-
-  if (cf->buffer != NULL) 
-    {
-      reader->buffer = cf->buffer;
-      cf->buffer = NULL; 
-    }
-  else 
-    {
-      reader->buffer = xnmalloc (cf->buffer_size, sizeof *cf->buffer);
-      memset (reader->buffer, 0, cf->buffer_size * sizeof *cf->buffer); 
-    }
-
-  case_create (&reader->c, cf->value_cnt);
+  assert (c->case_data->value_cnt >= casefile_get_value_cnt (cf));
 
-  reader->buffer_ofs = -1;
-  reader->file_ofs = -1;
-  seek_and_fill_buffer (reader);
+  return cf->class->append(cf, c);
 }
 
-/* Seeks the backing file for READER to the proper position and
-   refreshes the buffer contents. */
-static void
-seek_and_fill_buffer (struct casereader *reader) 
+/* Appends case C to casefile CF, which takes over ownership of
+   C.  
+   Returns true if successful, false if an I/O error occurred. */
+bool 
+casefile_append_xfer (struct casefile *cf, struct ccase *c)
 {
-  struct casefile *cf = reader->cf;
-  off_t new_ofs;
-
-  if (cf->value_cnt != 0) 
-    {
-      size_t buffer_case_cnt = cf->buffer_size / cf->value_cnt;
-      new_ofs = ((off_t) reader->case_idx / buffer_case_cnt
-                  * cf->buffer_size * sizeof *cf->buffer);
-      reader->buffer_pos = (reader->case_idx % buffer_case_cnt
-                            * cf->value_cnt);
-    }
-  else 
-    new_ofs = 0;
-  if (new_ofs != reader->file_ofs) 
-    {
-      if (lseek (reader->fd, new_ofs, SEEK_SET) != new_ofs)
-        io_error (cf, _("%s: Seeking temporary file: %s."),
-                  cf->file_name, strerror (errno));
-      else
-        reader->file_ofs = new_ofs;
-    }
+  assert (c->case_data->value_cnt >= casefile_get_value_cnt (cf));
 
-  if (cf->case_cnt > 0 && cf->value_cnt > 0 && reader->buffer_ofs != new_ofs)
-    fill_buffer (reader);
+  return cf->class->append_xfer(cf, c);
 }
 
-/* Fills READER's buffer by reading a block from disk. */
-static bool
-fill_buffer (struct casereader *reader)
-{
-  if (reader->cf->ok) 
-    {
-      int bytes = full_read (reader->fd, reader->buffer,
-                             reader->cf->buffer_size * sizeof *reader->buffer);
-      if (bytes < 0) 
-        io_error (reader->cf, _("%s: Reading temporary file: %s."),
-                  reader->cf->file_name, strerror (errno));
-      else if (bytes != reader->cf->buffer_size * sizeof *reader->buffer) 
-        io_error (reader->cf, _("%s: Temporary file ended unexpectedly."),
-                  reader->cf->file_name);
-      else 
-        {
-          reader->buffer_ofs = reader->file_ofs;
-          reader->file_ofs += bytes; 
-        }
-    }
-  return reader->cf->ok;
-}
 
-/* Returns the casefile that READER reads. */
-const struct casefile *
-casereader_get_casefile (const struct casereader *reader) 
-{
-  assert (reader != NULL);
+/* Puts a casefile to "sleep", that is, minimizes the resources
+   needed for it by closing its file descriptor and freeing its
+   buffer.  This is useful if we need so many casefiles that we
+   might not have enough memory and file descriptors to go
+   around.
   
-  return reader->cf;
-}
+   Implementations may choose to silently ignore this function.
 
-/* Reads a copy of the next case from READER into C.
-   Caller is responsible for destroying C.
-   Returns true if successful, false at end of file. */
+   Returns true if successful, false if an I/O error occurred. */
 bool
-casereader_read (struct casereader *reader, struct ccase *c) 
+casefile_sleep (const struct casefile *cf)
 {
-  assert (reader != NULL);
-  
-  if (!reader->cf->ok || reader->case_idx >= reader->cf->case_cnt) 
-    return false;
-
-  if (reader->cf->storage == MEMORY) 
-    {
-      size_t block_idx = reader->case_idx / CASES_PER_BLOCK;
-      size_t case_idx = reader->case_idx % CASES_PER_BLOCK;
-
-      case_clone (c, &reader->cf->cases[block_idx][case_idx]);
-      reader->case_idx++;
-      return true;
-    }
-  else 
-    {
-      if (reader->buffer_pos + reader->cf->value_cnt > reader->cf->buffer_size)
-        {
-          if (!fill_buffer (reader))
-            return false;
-          reader->buffer_pos = 0;
-        }
-
-      case_from_values (&reader->c, reader->buffer + reader->buffer_pos,
-                        reader->cf->value_cnt);
-      reader->buffer_pos += reader->cf->value_cnt;
-      reader->case_idx++;
-
-      case_clone (c, &reader->c);
-      return true;
-    }
+  return cf->class->sleep ? cf->class->sleep(cf) : true;
 }
 
-/* Reads the next case from READER into C and transfers ownership
-   to the caller.  Caller is responsible for destroying C.
-   Returns true if successful, false at end of file or on I/O
-   error. */
+/* Returns true only if casefile CF is stored in memory (instead of on
+   disk), false otherwise. */
 bool
-casereader_read_xfer (struct casereader *reader, struct ccase *c)
+casefile_in_core (const struct casefile *cf)
 {
-  assert (reader != NULL);
-
-  if (reader->destructive == 0
-      || reader->case_idx >= reader->cf->case_cnt
-      || reader->cf->storage == DISK) 
-    return casereader_read (reader, c);
-  else 
-    {
-      size_t block_idx = reader->case_idx / CASES_PER_BLOCK;
-      size_t case_idx = reader->case_idx % CASES_PER_BLOCK;
-      struct ccase *read_case = &reader->cf->cases[block_idx][case_idx];
-
-      case_move (c, read_case);
-      reader->case_idx++;
-      return true;
-    }
+  return cf->class->in_core(cf);
 }
 
-/* Sets the next case to be read by READER to CASE_IDX,
-   which must be less than the number of cases in the casefile.
-   Allowed only for random readers. */
-void
-casereader_seek (struct casereader *reader, unsigned long case_idx) 
+/* If CF is currently stored in memory, writes it to disk.  Readers, if any,
+   retain their current positions.
+   Returns true if successful, false if an I/O error occurred. */
+bool 
+casefile_to_disk (const struct casefile *cf)
 {
-  assert (reader != NULL);
-  assert (reader->random);
-  assert (case_idx < reader->cf->case_cnt);
-
-  reader->case_idx = case_idx;
-  if (reader->cf->storage == DISK)
-    seek_and_fill_buffer (reader);
+  return cf->class->to_disk(cf);
 }
 
-/* Destroys READER. */
 void
-casereader_destroy (struct casereader *reader)
+casereader_register(struct casefile *cf, 
+		    struct casereader *reader, 
+		    const struct class_casereader *class)
 {
-  assert (reader != NULL);
-
-  if (reader->next != NULL)
-    reader->next->prev = reader->prev;
-  if (reader->prev != NULL)
-    reader->prev->next = reader->next;
-  if (reader->cf->readers == reader)
-    reader->cf->readers = reader->next;
-
-  if (reader->cf->buffer == NULL)
-    reader->cf->buffer = reader->buffer;
-  else
-    free (reader->buffer);
-
-  if (reader->fd != -1) 
-    {
-      if (reader->cf->fd == -1)
-        reader->cf->fd = reader->fd;
-      else
-        safe_close (reader->fd);
-    }
-  
-  case_destroy (&reader->c);
-
-  free (reader);
-}
-
-/* Marks CF as having encountered an I/O error.
-   If this is the first error on CF, reports FORMAT to the user,
-   doing printf()-style substitutions. */
-static void
-io_error (struct casefile *cf, const char *format, ...)
-{
-  if (cf->ok) 
-    {
-      struct msg m;
-      va_list args;
-
-      m.category = MSG_GENERAL;
-      m.severity = MSG_ERROR;
-      m.where.file_name = NULL;
-      m.where.line_number = -1;
-      va_start (args, format);
-      m.text = xvasprintf (format, args);
-      va_end (args);
+  reader->class = class;
+  reader->cf = cf;
       
-      msg_emit (&m);
-    }
-  cf->ok = false;
+  ll_push_head (&cf->reader_list, &reader->ll);
 }
 
-/* Calls open(), passing FILE_NAME and FLAGS, repeating as necessary
-   to deal with interrupted calls. */
-static int
-safe_open (const char *file_name, int flags) 
+struct casereader *
+casereader_cast(struct casereader *cr)
 {
-  int fd;
-
-  do 
-    {
-      fd = open (file_name, flags);
-    }
-  while (fd == -1 && errno == EINTR);
-
-  return fd;
+  return cr;
 }
 
-/* Calls close(), passing FD, repeating as necessary to deal with
-   interrupted calls. */
-static int safe_close (int fd) 
+struct casefile *
+casefile_cast(struct casefile *cf)
 {
-  int retval;
-
-  do 
-    {
-      retval = close (fd);
-    }
-  while (retval == -1 && errno == EINTR);
-
-  return retval;
+  return cf;
 }
 
-/* Registers our exit handler with atexit() if it has not already
-   been registered. */
-static void
-register_atexit (void) 
+const struct casefile *
+const_casefile_cast(const struct casefile *cf)
 {
-  static bool registered = false;
-  if (!registered) 
-    {
-      registered = true;
-      atexit (exit_handler);
-    }
+  return cf;
 }
 
-/* atexit() handler that closes and deletes our temporary
-   files. */
-static void
-exit_handler (void) 
-{
-  while (casefiles != NULL)
-    casefile_destroy (casefiles);
-}
Index: src/data/casefile.h
===================================================================
RCS file: /cvsroot/pspp/pspp/src/data/casefile.h,v
retrieving revision 1.3
diff -u -p -r1.3 casefile.h
--- src/data/casefile.h	7 Jun 2006 01:48:47 -0000	1.3
+++ src/data/casefile.h	13 Jul 2006 05:27:03 -0000
@@ -1,5 +1,5 @@
 /* PSPP - computes sample statistics.
-   Copyright (C) 2004 Free Software Foundation, Inc.
+   Copyright (C) 2004, 2006 Free Software Foundation, Inc.
    Written by Ben Pfaff <blp@gnu.org>.
 
    This program is free software; you can redistribute it and/or
@@ -17,42 +17,51 @@
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
    02110-1301, USA. */
 
-#ifndef HEADER_CASEFILE
-#define HEADER_CASEFILE
+#ifndef CASEFILE_H
+#define CASEFILE_H
 
+#include <config.h>
 #include <stddef.h>
 #include <stdbool.h>
 
+
 struct ccase;
-struct casefile;
 struct casereader;
+struct casefile;
+
+
+
+struct casefile *casereader_get_casefile (const struct casereader *r);
+
+unsigned long casereader_cnum (const struct casereader *r);
+
+bool casereader_read (struct casereader *r, struct ccase *c);
+
+bool casereader_read_xfer (struct casereader *r, struct ccase *c);
+
+void casereader_destroy (struct casereader *r);
+
+void casefile_destroy (struct casefile *cf);
+
+bool casefile_error (const struct casefile *cf);
+
+unsigned long casefile_get_case_cnt (const struct casefile *cf);
+
+size_t casefile_get_value_cnt (const struct casefile *cf);
 
-struct casefile *casefile_create (size_t value_cnt);
-void casefile_destroy (struct casefile *);
+struct casereader *casefile_get_reader (const struct casefile *cf);
+struct casereader *casefile_get_destructive_reader (struct casefile *cf);
 
-bool casefile_error (const struct casefile *);
-bool casefile_in_core (const struct casefile *);
-bool casefile_to_disk (const struct casefile *);
-bool casefile_sleep (const struct casefile *);
 
-size_t casefile_get_value_cnt (const struct casefile *);
-unsigned long casefile_get_case_cnt (const struct casefile *);
 
-bool casefile_append (struct casefile *, const struct ccase *);
-bool casefile_append_xfer (struct casefile *, struct ccase *);
+bool casefile_append (struct casefile *cf, const struct ccase *c);
 
-void casefile_mode_reader (struct casefile *);
-struct casereader *casefile_get_reader (const struct casefile *);
-struct casereader *casefile_get_destructive_reader (struct casefile *);
-struct casereader *casefile_get_random_reader (const struct casefile *);
+bool casefile_append_xfer (struct casefile *cf, struct ccase *c);
 
-const struct casefile *casereader_get_casefile (const struct casereader *);
-bool casereader_read (struct casereader *, struct ccase *);
-bool casereader_read_xfer (struct casereader *, struct ccase *);
-void casereader_destroy (struct casereader *);
+bool casefile_sleep (const struct casefile *cf);
 
-void casereader_seek (struct casereader *, unsigned long case_idx);
+bool casefile_in_core (const struct casefile *cf);
 
-unsigned long casereader_cnum(const struct casereader *);
+bool casefile_to_disk (const struct casefile *cf);
 
-#endif /* casefile.h */
+#endif
Index: src/data/fastfile.c
===================================================================
RCS file: src/data/fastfile.c
diff -N src/data/fastfile.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ src/data/fastfile.c	13 Jul 2006 05:27:04 -0000
@@ -0,0 +1,816 @@
+/* PSPP - computes sample statistics.
+   Copyright (C) 2004, 2006 Free Software Foundation, Inc.
+   Written by Ben Pfaff <blp@gnu.org>.
+
+   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., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301, USA. */
+
+#include <config.h>
+#include "casefile.h"
+#include "casefile-private.h"
+#include "fastfile.h"
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <libpspp/alloc.h>
+#include "case.h"
+#include <libpspp/compiler.h>
+#include <libpspp/message.h>
+#include "full-read.h"
+#include "full-write.h"
+#include <libpspp/misc.h>
+#include "make-file.h"
+#include "settings.h"
+#include "variable.h"
+
+#include "gettext.h"
+#define _(msgid) gettext (msgid)
+
+#define IO_BUF_SIZE (8192 / sizeof (union value))
+
+/* A fastfile represents a sequentially accessible stream of
+   immutable cases.
+
+   If workspace allows, a fastfile is maintained in memory.  If
+   workspace overflows, then the fastfile is pushed to disk.  In
+   either case the interface presented to callers is kept the
+   same.
+
+   The life cycle of a fastfile consists of up to three phases:
+
+       1. Writing.  The fastfile initially contains no cases.  In
+          this phase, any number of cases may be appended to the
+          end of a fastfile.  (Cases are never inserted in the
+          middle or before the beginning of a fastfile.)
+
+          Use casefile_append or casefile_append_xfer to
+          append a case to a fastfile.
+
+       2. Reading.  The fastfile may be read sequentially,
+          starting from the beginning, by "casereaders".  Any
+          number of casereaders may be created, at any time,
+          during the reading phase.  Each casereader has an
+          independent position in the fastfile.
+
+          Ordinary casereaders may only move forward.  They
+          cannot move backward to arbitrary records or seek
+          randomly.  Cloning casereaders is possible, but it is
+          not yet implemented.
+
+          Use casefile_get_reader to create a casereader for
+          use in phase 2.  This also transitions from phase 1 to
+          phase 2.  Calling fastfile_mode_reader makes the same
+          transition, without creating a casereader.
+
+          Use casereader_read or casereader_read_xfer to read
+          a case from a casereader.  Use casereader_destroy to
+          discard a casereader when it is no longer needed.
+
+       3. Destruction.  This phase is optional.  The fastfile is
+          also read with casereaders in this phase, but the
+          ability to create new casereaders is curtailed.
+
+          In this phase, casereaders could still be cloned (once
+          we eventually implement cloning).
+
+          To transition from phase 1 or 2 to phase 3 and create a
+          casereader, call casefile_get_destructive_reader().
+          The same functions apply to the casereader obtained
+          this way as apply to casereaders obtained in phase 2.
+          
+          After casefile_get_destructive_reader is called, no
+          more casereaders may be created.  (If cloning of
+          casereaders were implemented, it would still be
+          possible.)
+
+          The purpose of the limitations applied to casereaders
+          in phase 3 is to allow in-memory fastfiles to fully
+          transfer ownership of cases to the casereaders,
+          avoiding the need for extra copies of case data.  For
+          relatively static data sets with many variables, I
+          suspect (without evidence) that this may be a big
+          performance boost.
+
+   When a fastfile is no longer needed, it may be destroyed with
+   casefile_destroy.  This function will also destroy any
+   remaining casereaders. */
+
+/* FIXME: should we implement compression? */
+
+/* In-memory cases are arranged in an array of arrays.  The top
+   level is variable size and the size of each bottom level array
+   is fixed at the number of cases defined here.  */
+#define CASES_PER_BLOCK 128
+
+
+static const struct class_casefile class;
+
+/* A fastfile. */
+struct fastfile
+{
+  struct casefile cf;		/* Parent */
+
+  size_t value_cnt;		/* Case size in `union value's. */
+  size_t case_acct_size;	/* Case size for accounting. */
+  unsigned long case_cnt;	/* Number of cases stored. */
+  enum { MEMORY, DISK } storage;	/* Where cases are stored. */
+  enum { WRITE, READ } mode;		/* Is writing or reading allowed? */
+
+  bool being_destroyed;		/* Does a destructive reader exist? */
+  bool ok;			/* False after I/O error. */
+
+  /* Memory storage. */
+  struct ccase **cases;		/* Pointer to array of cases. */
+
+  /* Disk storage. */
+  int fd;			/* File descriptor, -1 if none. */
+  char *file_name;		/* File name. */
+  union value *buffer;		/* I/O buffer, NULL if none. */
+  size_t buffer_used;		/* Number of values used in buffer. */
+  size_t buffer_size;		/* Buffer size in values. */
+};
+
+
+static const struct class_casereader class_reader;
+
+/* For reading out the cases in a fastfile. */
+struct fastfilereader
+{
+  struct casereader cr;		/* Parent */
+
+  unsigned long case_idx;	/* Case number of current case. */
+  bool destructive;		/* Is this a destructive reader? */
+
+  /* Disk storage. */
+  int fd;			/* File descriptor. */
+  off_t file_ofs;		/* Current position in fd. */
+  off_t buffer_ofs;		/* File offset of buffer start. */
+  union value *buffer;		/* I/O buffer. */
+  size_t buffer_pos;		/* Offset of buffer position. */
+  struct ccase c;		/* Current case. */
+};
+
+
+static void io_error (struct fastfile *, const char *, ...) 
+     PRINTF_FORMAT (2, 3);
+static int safe_open (const char *file_name, int flags);
+static int safe_close (int fd);
+static void write_case_to_disk (struct fastfile *, const struct ccase *);
+static void flush_buffer (struct fastfile *);
+
+static void reader_open_file (struct fastfilereader *);
+
+static void seek_and_fill_buffer (struct fastfilereader *);
+static bool fill_buffer (struct fastfilereader *);
+
+
+/* Number of bytes of case allocated in in-memory fastfiles. */
+static size_t case_bytes;
+
+/* Destroys READER. */
+static void fastfilereader_destroy (struct casereader *cr)
+{
+  struct fastfilereader *reader = (struct fastfilereader *) cr;
+  struct fastfile *ff = (struct fastfile *) casereader_get_casefile (cr);
+
+  if (ff->buffer == NULL)
+    ff->buffer = reader->buffer;
+  else
+    free (reader->buffer);
+
+  if (reader->fd != -1)
+    {
+      if (ff->fd == -1)
+	ff->fd = reader->fd;
+      else
+	safe_close (reader->fd);
+    }
+
+  case_destroy (&reader->c);
+
+  free (reader);
+}
+
+/* Return the case number of the current case */
+static unsigned long
+fastfilereader_cnum (const struct casereader *cr)
+{
+  const struct fastfilereader *ffr = (const struct fastfilereader *) cr;
+  return ffr->case_idx;
+}
+
+
+/* Reads a copy of the next case from READER into C.
+   Caller is responsible for destroying C.
+   Returns true if successful, false at end of file. */
+static bool
+fastfilereader_read (struct casereader *cr, struct ccase *c)
+{
+  struct fastfilereader *reader = (struct fastfilereader *) cr;
+  struct fastfile *ff = (struct fastfile *) casereader_get_casefile (cr);
+
+  if (!ff->ok || reader->case_idx >= ff->case_cnt)
+    return false;
+
+  if (ff->storage == MEMORY)
+    {
+      size_t block_idx = reader->case_idx / CASES_PER_BLOCK;
+      size_t case_idx = reader->case_idx % CASES_PER_BLOCK;
+
+      case_clone (c, &ff->cases[block_idx][case_idx]);
+      reader->case_idx++;
+      return true;
+    }
+  else
+    {
+      if (reader->buffer_pos + ff->value_cnt > ff->buffer_size)
+	{
+	  if (!fill_buffer (reader))
+	    return false;
+	  reader->buffer_pos = 0;
+	}
+
+      case_from_values (&reader->c, reader->buffer + reader->buffer_pos,
+			ff->value_cnt);
+      reader->buffer_pos += ff->value_cnt;
+      reader->case_idx++;
+
+      case_clone (c, &reader->c);
+      return true;
+    }
+}
+
+
+
+/* Reads the next case from READER into C and transfers ownership
+   to the caller.  Caller is responsible for destroying C.
+   Returns true if successful, false at end of file or on I/O
+   error. */
+static bool
+fastfilereader_read_xfer (struct casereader *reader, struct ccase *c)
+{
+  struct fastfilereader *ffr = (struct fastfilereader *) reader;
+  struct fastfile *ff = (struct fastfile *) casereader_get_casefile (reader);
+
+  if (ffr->destructive == 0
+      || ffr->case_idx >= ff->case_cnt || ff->storage == DISK)
+    {
+      return fastfilereader_read (reader, c);
+    }
+  else
+    {
+      size_t block_idx = ffr->case_idx / CASES_PER_BLOCK;
+      size_t case_idx = ffr->case_idx % CASES_PER_BLOCK;
+      struct ccase *read_case = &ff->cases[block_idx][case_idx];
+
+      case_move (c, read_case);
+      ffr->case_idx++;
+      return true;
+    }
+}
+
+
+
+/* Creates and returns a casereader for CF.  A casereader can be used to
+   sequentially read the cases in a fastfile. */
+static struct casereader *
+fastfile_get_reader (const struct casefile *cf_)
+{
+  struct casefile *cf = (struct casefile *) cf_;
+  struct fastfilereader *ffr = xzalloc (sizeof *ffr);
+  struct casereader *reader = (struct casereader *) ffr;
+  struct fastfile *ff = (struct fastfile *) cf;
+
+  assert (!ff->being_destroyed);
+
+  /* Flush the buffer to disk if it's not empty. */
+  if (ff->mode == WRITE && ff->storage == DISK)
+    flush_buffer (ff);
+
+  ff->mode = READ;
+
+  casereader_register (cf, reader, &class_reader);
+
+  ffr->case_idx = 0;
+  ffr->destructive = 0;
+  ffr->fd = -1;
+  ffr->buffer = NULL;
+  ffr->buffer_pos = 0;
+  case_nullify (&ffr->c);
+
+  if (ff->storage == DISK)
+    reader_open_file (ffr);
+
+  return reader;
+}
+
+
+/* Creates and returns a destructive casereader for CF.  Like a
+   normal casereader, a destructive casereader sequentially reads
+   the cases in a fastfile.  Unlike a normal casereader, a
+   destructive reader cannot operate concurrently with any other
+   reader.  (This restriction could be relaxed in a few ways, but
+   it is so far unnecessary for other code.) */
+static struct casereader *
+fastfile_get_destructive_reader (struct casefile *cf)
+{
+  struct fastfile *ff = (struct fastfile *) cf;
+  struct casereader *cfr = fastfile_get_reader (cf);
+  struct fastfilereader *ffr = (struct fastfilereader *) cfr;
+  ffr->destructive = 1;
+  ff->being_destroyed = 1;
+  return cfr;
+}
+
+
+/* Returns the number of `union value's in a case for CF. */
+static size_t
+fastfile_get_value_cnt (const struct casefile *cf)
+{
+  const struct fastfile *ff = (const struct fastfile *) cf;
+  return ff->value_cnt;
+}
+
+/* Appends a copy of case C to fastfile CF.  Not valid after any
+   reader for CF has been created.
+   Returns true if successful, false if an I/O error occurred. */
+static bool
+fastfile_append (struct casefile *cf, const struct ccase *c)
+{
+  struct fastfile *ff = (struct fastfile *) cf;
+  assert (ff->mode == WRITE);
+  assert (c != NULL);
+
+  /* Try memory first. */
+  if (ff->storage == MEMORY)
+    {
+      if (case_bytes < get_workspace ())
+	{
+	  size_t block_idx = ff->case_cnt / CASES_PER_BLOCK;
+	  size_t case_idx = ff->case_cnt % CASES_PER_BLOCK;
+	  struct ccase new_case;
+
+	  case_bytes += ff->case_acct_size;
+	  case_clone (&new_case, c);
+	  if (case_idx == 0)
+	    {
+	      if ((block_idx & (block_idx - 1)) == 0)
+		{
+		  size_t block_cap = block_idx == 0 ? 1 : block_idx * 2;
+		  ff->cases = xnrealloc (ff->cases,
+					 block_cap, sizeof *ff->cases);
+		}
+
+	      ff->cases[block_idx] = xnmalloc (CASES_PER_BLOCK,
+					       sizeof **ff->cases);
+	    }
+
+	  case_move (&ff->cases[block_idx][case_idx], &new_case);
+	}
+      else
+	{
+	  casefile_to_disk (cf);
+	  assert (ff->storage == DISK);
+	  write_case_to_disk (ff, c);
+	}
+    }
+  else
+    write_case_to_disk (ff, c);
+
+  ff->case_cnt++;
+  return ff->ok;
+}
+
+
+/* Appends case C to fastfile CF, which takes over ownership of
+   C.  Not valid after any reader for CF has been created.
+   Returns true if successful, false if an I/O error occurred. */
+static bool
+fastfile_append_xfer (struct casefile *cf, struct ccase *c)
+{
+  struct fastfile *ff = (struct fastfile *) cf;
+  fastfile_append (cf, c);
+  case_destroy (c);
+  return ff->ok;
+}
+
+/* Returns the number of cases in fastfile CF. */
+static unsigned long
+fastfile_get_case_cnt (const struct casefile *cf)
+{
+  const struct fastfile *ff = (const struct fastfile *) cf;
+  return ff->case_cnt;
+}
+
+
+/* Returns true only if fastfile CF is stored in memory (instead of on
+   disk), false otherwise. */
+static bool
+fastfile_in_core (const struct casefile *cf)
+{
+  const struct fastfile *ff = (const struct fastfile *) cf;
+  return (ff->storage == MEMORY);
+}
+
+
+/* If CF is currently stored in memory, writes it to disk.  Readers, if any,
+   retain their current positions.
+   Returns true if successful, false if an I/O error occurred. */
+static bool
+fastfile_to_disk (const struct casefile *cf_)
+{
+  struct fastfile *ff = (struct fastfile *) cf_;
+  struct casefile *cf = &ff->cf;
+
+  if (ff->storage == MEMORY)
+    {
+      size_t idx, block_cnt;
+      struct casereader *reader;
+
+      assert (ff->file_name == NULL);
+      assert (ff->fd == -1);
+      assert (ff->buffer_used == 0);
+
+      if (!make_temp_file (&ff->fd, &ff->file_name))
+	{
+	  ff->ok = false;
+	  return false;
+	}
+      ff->storage = DISK;
+
+      ff->buffer = xnmalloc (ff->buffer_size, sizeof *ff->buffer);
+      memset (ff->buffer, 0, ff->buffer_size * sizeof *ff->buffer);
+
+      case_bytes -= ff->case_cnt * ff->case_acct_size;
+      for (idx = 0; idx < ff->case_cnt; idx++)
+	{
+	  size_t block_idx = idx / CASES_PER_BLOCK;
+	  size_t case_idx = idx % CASES_PER_BLOCK;
+	  struct ccase *c = &ff->cases[block_idx][case_idx];
+	  write_case_to_disk (ff, c);
+	  case_destroy (c);
+	}
+
+      block_cnt = DIV_RND_UP (ff->case_cnt, CASES_PER_BLOCK);
+      for (idx = 0; idx < block_cnt; idx++)
+	free (ff->cases[idx]);
+
+      free (ff->cases);
+      ff->cases = NULL;
+
+      if (ff->mode == READ)
+	flush_buffer (ff);
+
+      ll_for_each (reader, struct casereader, ll, &cf->reader_list)
+	reader_open_file ((struct fastfilereader *) reader);
+
+    }
+  return ff->ok;
+}
+
+/* Puts a fastfile to "sleep", that is, minimizes the resources
+   needed for it by closing its file descriptor and freeing its
+   buffer.  This is useful if we need so many fastfiles that we
+   might not have enough memory and file descriptors to go
+   around.
+
+   For simplicity, this implementation always converts the
+   fastfile to reader mode.  If this turns out to be a problem,
+   with a little extra work we could also support sleeping
+   writers.
+
+   Returns true if successful, false if an I/O error occurred. */
+static bool
+fastfile_sleep (const struct casefile *cf_)
+{
+  struct fastfile *ff = (struct fastfile *) cf_;
+  struct casefile *cf = &ff->cf;
+
+  fastfile_to_disk (cf);
+  flush_buffer (ff);
+
+  if (ff->fd != -1)
+    {
+      safe_close (ff->fd);
+      ff->fd = -1;
+    }
+  if (ff->buffer != NULL)
+    {
+      free (ff->buffer);
+      ff->buffer = NULL;
+    }
+
+  return ff->ok;
+}
+
+
+/* Returns true if an I/O error has occurred in fastfile CF. */
+static bool
+fastfile_error (const struct casefile *cf)
+{
+  const struct fastfile *ff = (const struct fastfile *) cf;
+  return !ff->ok;
+}
+
+/* Destroys fastfile CF. */
+static void
+fastfile_destroy (struct casefile *cf)
+{
+  struct fastfile *ff = (struct fastfile *) cf;
+
+  if (cf != NULL)
+    {
+      if (ff->cases != NULL)
+	{
+	  size_t idx, block_cnt;
+
+	  case_bytes -= ff->case_cnt * ff->case_acct_size;
+	  for (idx = 0; idx < ff->case_cnt; idx++)
+	    {
+	      size_t block_idx = idx / CASES_PER_BLOCK;
+	      size_t case_idx = idx % CASES_PER_BLOCK;
+	      struct ccase *c = &ff->cases[block_idx][case_idx];
+	      case_destroy (c);
+	    }
+
+	  block_cnt = DIV_RND_UP (ff->case_cnt, CASES_PER_BLOCK);
+	  for (idx = 0; idx < block_cnt; idx++)
+	    free (ff->cases[idx]);
+
+	  free (ff->cases);
+	}
+
+      if (ff->fd != -1)
+	safe_close (ff->fd);
+
+      if (ff->file_name != NULL && remove (ff->file_name) == -1)
+	io_error (ff, _("%s: Removing temporary file: %s."),
+		  ff->file_name, strerror (errno));
+      free (ff->file_name);
+
+      free (ff->buffer);
+
+      free (ff);
+    }
+}
+
+
+/* Creates and returns a fastfile to store cases of VALUE_CNT
+   `union value's each. */
+struct casefile *
+fastfile_create (size_t value_cnt)
+{
+  struct fastfile *ff = xzalloc (sizeof *ff);
+  struct casefile *cf = &ff->cf;
+
+  casefile_register (cf, &class);
+
+  ff->value_cnt = value_cnt;
+  ff->case_acct_size = (ff->value_cnt + 4) * sizeof *ff->buffer;
+  ff->case_cnt = 0;
+  ff->storage = MEMORY;
+  ff->mode = WRITE;
+  ff->being_destroyed = 0;
+  ff->ok = true;
+  ff->cases = NULL;
+  ff->fd = -1;
+  ff->file_name = NULL;
+  ff->buffer = NULL;
+  ff->buffer_size = ROUND_UP (ff->value_cnt, IO_BUF_SIZE);
+  if (ff->value_cnt > 0 && ff->buffer_size % ff->value_cnt > 64)
+    ff->buffer_size = ff->value_cnt;
+  ff->buffer_used = 0;
+
+  return cf;
+}
+
+
+
+/* Marks FF as having encountered an I/O error.
+   If this is the first error on CF, reports FORMAT to the user,
+   doing printf()-style substitutions. */
+static void
+io_error (struct fastfile *ff, const char *format, ...)
+{
+  if (ff->ok)
+    {
+      struct msg m;
+      va_list args;
+
+      m.category = MSG_GENERAL;
+      m.severity = MSG_ERROR;
+      m.where.file_name = NULL;
+      m.where.line_number = -1;
+      va_start (args, format);
+      m.text = xvasprintf (format, args);
+      va_end (args);
+
+      msg_emit (&m);
+    }
+  ff->ok = false;
+}
+
+/* Calls open(), passing FILE_NAME and FLAGS, repeating as necessary
+   to deal with interrupted calls. */
+static int
+safe_open (const char *file_name, int flags)
+{
+  int fd;
+
+  do
+    {
+      fd = open (file_name, flags);
+    }
+  while (fd == -1 && errno == EINTR);
+
+  return fd;
+}
+
+/* Calls close(), passing FD, repeating as necessary to deal with
+   interrupted calls. */
+static int
+safe_close (int fd)
+{
+  int retval;
+
+  do
+    {
+      retval = close (fd);
+    }
+  while (retval == -1 && errno == EINTR);
+
+  return retval;
+}
+
+
+/* Writes case C to fastfile CF's disk buffer, first flushing the buffer to
+   disk if it would otherwise overflow.
+   Returns true if successful, false if an I/O error occurred. */
+static void
+write_case_to_disk (struct fastfile *ff, const struct ccase *c)
+{
+  if (!ff->ok)
+    return;
+
+  case_to_values (c, ff->buffer + ff->buffer_used, ff->value_cnt);
+  ff->buffer_used += ff->value_cnt;
+  if (ff->buffer_used + ff->value_cnt > ff->buffer_size)
+    flush_buffer (ff);
+}
+
+
+/* If any bytes in FF's output buffer are used, flush them to
+   disk. */
+static void
+flush_buffer (struct fastfile *ff)
+{
+  if (ff->ok && ff->buffer_used > 0)
+    {
+      if (!full_write (ff->fd, ff->buffer,
+		       ff->buffer_size * sizeof *ff->buffer))
+	io_error (ff, _("Error writing temporary file: %s."),
+		  strerror (errno));
+      ff->buffer_used = 0;
+    }
+}
+
+
+/* Opens a disk file for READER and seeks to the current position as indicated
+   by case_idx.  Normally the current position is the beginning of the file,
+   but fastfile_to_disk may cause the file to be opened at a different
+   position. */
+static void
+reader_open_file (struct fastfilereader *reader)
+{
+  struct casefile *cf = casereader_get_casefile(&reader->cr);
+  struct fastfile *ff = (struct fastfile *) cf;
+  if (!ff->ok || reader->case_idx >= ff->case_cnt)
+    return;
+
+  if (ff->fd != -1)
+    {
+      reader->fd = ff->fd;
+      ff->fd = -1;
+    }
+  else
+    {
+      reader->fd = safe_open (ff->file_name, O_RDONLY);
+      if (reader->fd < 0)
+	io_error (ff, _("%s: Opening temporary file: %s."),
+		  ff->file_name, strerror (errno));
+    }
+
+  if (ff->buffer != NULL)
+    {
+      reader->buffer = ff->buffer;
+      ff->buffer = NULL;
+    }
+  else
+    {
+      reader->buffer = xnmalloc (ff->buffer_size, sizeof *ff->buffer);
+      memset (reader->buffer, 0, ff->buffer_size * sizeof *ff->buffer);
+    }
+
+  case_create (&reader->c, ff->value_cnt);
+
+  reader->buffer_ofs = -1;
+  reader->file_ofs = -1;
+  seek_and_fill_buffer (reader);
+}
+
+/* Seeks the backing file for READER to the proper position and
+   refreshes the buffer contents. */
+static void
+seek_and_fill_buffer (struct fastfilereader *reader)
+{
+  struct casefile *cf = casereader_get_casefile(&reader->cr);
+  struct fastfile *ff = (struct fastfile *) cf;
+  off_t new_ofs;
+
+  if (ff->value_cnt != 0)
+    {
+      size_t buffer_case_cnt = ff->buffer_size / ff->value_cnt;
+      new_ofs = ((off_t) reader->case_idx / buffer_case_cnt
+		 * ff->buffer_size * sizeof *ff->buffer);
+      reader->buffer_pos = (reader->case_idx % buffer_case_cnt
+			    * ff->value_cnt);
+    }
+  else
+    new_ofs = 0;
+  if (new_ofs != reader->file_ofs)
+    {
+      if (lseek (reader->fd, new_ofs, SEEK_SET) != new_ofs)
+	io_error (ff, _("%s: Seeking temporary file: %s."),
+		  ff->file_name, strerror (errno));
+      else
+	reader->file_ofs = new_ofs;
+    }
+
+  if (ff->case_cnt > 0 && ff->value_cnt > 0 && reader->buffer_ofs != new_ofs)
+    fill_buffer (reader);
+}
+
+/* Fills READER's buffer by reading a block from disk. */
+static bool
+fill_buffer (struct fastfilereader *reader)
+{
+  struct casefile *cf = casereader_get_casefile(&reader->cr);
+  struct fastfile *ff = (struct fastfile *) cf;
+  if (ff->ok)
+    {
+      int bytes = full_read (reader->fd, reader->buffer,
+			     ff->buffer_size *
+			     sizeof *reader->buffer);
+      if (bytes < 0)
+	io_error (ff, _("%s: Reading temporary file: %s."),
+		  ff->file_name, strerror (errno));
+      else if (bytes != ff->buffer_size * sizeof *reader->buffer)
+	io_error (ff, _("%s: Temporary file ended unexpectedly."),
+		  ff->file_name);
+      else
+	{
+	  reader->buffer_ofs = reader->file_ofs;
+	  reader->file_ofs += bytes;
+	}
+    }
+  return ff->ok;
+}
+
+static const struct class_casefile class = 
+  {
+    fastfile_destroy,
+    fastfile_error,
+    fastfile_get_value_cnt,
+    fastfile_get_case_cnt,
+    fastfile_get_reader,
+    fastfile_get_destructive_reader,
+    fastfile_append,
+    fastfile_append_xfer,
+    fastfile_sleep,
+    fastfile_in_core,
+    fastfile_to_disk,
+  };
+
+static const struct class_casereader class_reader = 
+  {
+    fastfilereader_read,
+    fastfilereader_read_xfer,
+    fastfilereader_cnum,
+    fastfilereader_destroy,
+  };
Index: src/data/fastfile.h
===================================================================
RCS file: src/data/fastfile.h
diff -N src/data/fastfile.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ src/data/fastfile.h	13 Jul 2006 05:27:04 -0000
@@ -0,0 +1,27 @@
+/* PSPP - computes sample statistics.
+   Copyright (C) 2004, 2006 Free Software Foundation, Inc.
+   Written by Ben Pfaff <blp@gnu.org>.
+
+   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., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301, USA. */
+
+#ifndef FASTFILE_H
+#define FASTFILE_H
+
+#include <config.h>
+
+struct casefile *fastfile_create (size_t value_cnt);
+
+#endif
Index: src/data/flexifile.c
===================================================================
RCS file: src/data/flexifile.c
diff -N src/data/flexifile.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ src/data/flexifile.c	13 Jul 2006 05:27:04 -0000
@@ -0,0 +1,423 @@
+/* PSPP - computes sample statistics.
+
+   Copyright (C) 2006 Free Software Foundation, Inc.
+   Written by John Darrington <john@darrington.wattle.id.au>
+
+   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., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301, USA. */
+
+#include <config.h>
+#include <xalloc.h>
+#include <assert.h>
+#include "flexifile.h"
+#include "casefile.h"
+#include "casefile-private.h"
+#include "case.h"
+
+
+struct class_flexifile
+{
+  struct class_casefile parent;
+  bool initialised;
+  unsigned long stamp;
+
+  bool (*get_case) (const struct flexifile *, unsigned long, struct ccase *);
+
+  bool (*insert_case) (struct flexifile *, struct ccase *, int );
+  bool (*delete_cases) (struct flexifile *, int, int );
+
+  bool (*resize) (struct flexifile *, int, int );
+};
+
+static struct class_flexifile class;
+
+#define CLASS_FLEXIFILE(K)  ((struct class_flexifile *) K)
+#define CONST_CLASS_FLEXIFILE(K) ((const struct class_flexifile *) K)
+
+
+/* A flexifile. */
+struct flexifile
+{
+  struct casefile cf;		/* Parent */
+
+  size_t value_cnt;		/* Case size in `union value's. */
+  unsigned long case_cnt;	/* Number of cases stored. */
+
+
+  /* Memory storage. */
+  struct ccase *cases;		/* Pointer to array of cases. */
+  unsigned long capacity;       /* size of array in cases */
+};
+
+struct class_flexifilereader 
+{
+  struct class_casereader parent ;
+
+  bool initialised;
+  unsigned long stamp;
+};
+
+static struct class_flexifilereader class_reader;
+
+/* For reading out the cases in a flexifile. */
+struct flexifilereader
+{
+  struct casereader cr;		/* Parent */
+
+  unsigned long case_idx;	/* Case number of current case. */
+  bool destructive;		/* Is this a destructive reader? */
+};
+
+
+
+#define CHUNK_SIZE 10
+
+static bool 
+impl_get_case(const struct flexifile *ff, unsigned long casenum, 
+	      struct ccase *);
+static bool
+impl_insert_case (struct flexifile *ff, struct ccase *c, int posn);
+
+static bool 
+impl_delete_cases (struct flexifile *ff, int n_cases, int first);
+
+static bool 
+impl_resize (struct flexifile *ff, int n_values, int posn);
+
+
+/* Gets a case, for which writing may not be safe */
+bool 
+flexifile_get_case(const struct flexifile *ff, unsigned long casenum, 
+		   struct ccase *c)
+{
+  const struct class_flexifile *class = 
+    CONST_CLASS_FLEXIFILE (CONST_CASEFILE(ff)->class) ;
+
+  return class->get_case(ff, casenum, c);
+}
+
+
+/* Insert N_VALUES before POSN.
+   If N_VALUES is negative, then deleted -N_VALUES instead
+*/
+bool
+flexifile_resize (struct flexifile *ff, int n_values, int posn)
+{
+  const struct class_flexifile *class = 
+    CONST_CLASS_FLEXIFILE (CONST_CASEFILE(ff)->class) ;
+
+  return class->resize(ff, n_values, posn);
+}
+
+
+
+bool
+flexifile_insert_case (struct flexifile *ff, struct ccase *c, int posn)
+{
+  const struct class_flexifile *class = 
+    CONST_CLASS_FLEXIFILE (CONST_CASEFILE(ff)->class) ;
+
+  return class->insert_case(ff, c, posn);
+}
+
+
+bool
+flexifile_delete_cases (struct flexifile *ff, int n_cases, int first)
+{
+  const struct class_flexifile *class = 
+    CONST_CLASS_FLEXIFILE (CONST_CASEFILE(ff)->class) ;
+
+  return class->delete_cases (ff, n_cases, first);
+}
+
+
+static unsigned long 
+flexifile_get_case_cnt (const struct casefile *cf)
+{
+  return FLEXIFILE(cf)->case_cnt;
+}
+
+static size_t
+flexifile_get_value_cnt (const struct casefile *cf)
+{
+  return FLEXIFILE(cf)->value_cnt;
+}
+
+
+static void
+flexifile_destroy (struct casefile *cf)
+{
+  int i ; 
+  for ( i = 0 ; i < FLEXIFILE(cf)->case_cnt; ++i ) 
+    case_destroy( &FLEXIFILE(cf)->cases[i]);
+
+  free(FLEXIFILE(cf)->cases);
+}
+
+static void
+grow(struct flexifile *ff) 
+{
+  ff->capacity += CHUNK_SIZE;
+  ff->cases = xrealloc(ff->cases, ff->capacity * sizeof ( *ff->cases) );
+}
+
+static bool
+flexifile_append (struct casefile *cf, const struct ccase *c)
+{
+  struct flexifile *ff =  FLEXIFILE(cf);
+
+  if (ff->case_cnt >= ff->capacity)
+    grow(ff);
+
+  case_clone (&ff->cases[ff->case_cnt++], c);
+
+  return true;
+}
+
+/* Reads a copy of the next case from READER into C.
+   Caller is responsible for destroying C.
+   Returns true if successful, false at end of file. */
+static bool
+flexifilereader_read (struct casereader *cr, struct ccase *c)
+{
+  struct flexifilereader *ffr = FLEXIFILEREADER(cr);
+  struct flexifile *ff = FLEXIFILE(casereader_get_casefile(cr));
+
+  if ( ffr->case_idx >= ff->case_cnt) 
+    return false;
+
+  case_clone (c, &ff->cases[ffr->case_idx++]);
+
+  return true;
+}
+
+
+static bool
+flexifilereader_read_xfer (struct casereader *cr, struct ccase *c)
+{
+  struct flexifile *ff;
+  struct flexifilereader *ffr = FLEXIFILEREADER (cr);
+  assert (ffr);
+
+  ff = FLEXIFILE(casereader_get_casefile(cr));
+
+  if (ffr->destructive == 0
+      || ffr->case_idx >= ff->case_cnt )
+    {
+      return flexifilereader_read (cr, c);
+    }
+  else
+    {
+      struct ccase *read_case = &ff->cases[ffr->case_idx];
+
+      case_move (c, read_case);
+      ffr->case_idx++;
+      return true;
+    }
+}
+
+static void
+flexifilereader_destroy(struct casereader *r)
+{
+  free(r);
+}
+
+static struct casereader * 
+flexifile_get_reader (const struct casefile *cf_)
+{
+  struct casefile *cf = (struct casefile *) cf_;
+  struct flexifilereader *ffr = xzalloc (sizeof *ffr);
+  struct casereader *reader = (struct casereader *) ffr;
+
+  if ( ! class_reader.initialised ) 
+    {
+      class_reader.stamp = random ();
+      class_reader.initialised = true;
+
+      class_reader.parent.read = flexifilereader_read;
+      class_reader.parent.read_xfer = flexifilereader_read_xfer;
+      class_reader.parent.destroy = flexifilereader_destroy;
+    }
+
+  casereader_register (cf, reader, CLASS_CASEREADER(&class_reader));
+
+  return reader;
+}
+
+static bool
+flexifile_in_core(const struct casefile *cf UNUSED)
+{
+  /* Always in memory */
+  return true;
+}
+
+struct casefile *
+flexifile_create (size_t value_cnt)
+{
+  struct flexifile *ff = xzalloc (sizeof *ff);
+  struct casefile *cf = (struct casefile *) ff;
+
+  if ( ! class.initialised ) 
+    {
+      class.stamp = random ();
+      class.initialised = true;
+
+      class.parent.get_case_cnt = flexifile_get_case_cnt;
+      class.parent.destroy = flexifile_destroy;
+      class.parent.append = flexifile_append;
+      class.parent.get_reader = flexifile_get_reader;
+      class.parent.in_core = flexifile_in_core;
+      class.parent.get_value_cnt = flexifile_get_value_cnt;
+
+      class.get_case = impl_get_case ;
+      class.insert_case = impl_insert_case ;
+      class.delete_cases = impl_delete_cases;
+      class.resize = impl_resize;
+    }
+
+  casefile_register (cf, (struct class_casefile *) &class);
+
+  ff->value_cnt = value_cnt;
+ 
+  ff->cases = xzalloc(sizeof (struct ccase *) * CHUNK_SIZE);
+  ff->capacity = CHUNK_SIZE;
+ 
+  return cf;
+}
+
+/* Casts */
+
+struct flexifilereader *
+flexifilereader_cast(const struct casereader *cr)
+{
+  struct class_flexifilereader *kr = 
+    (struct class_flexifilereader *) cr->class;
+  
+  assert ( kr->stamp == class_reader.stamp );
+  return (struct flexifilereader *) cr;
+}
+
+struct flexifile *
+flexifile_cast(const struct casefile *cf)
+{
+  struct class_flexifile *kf = (struct class_flexifile *) cf->class;
+  
+  assert ( kf->stamp == class.stamp );
+  return (struct flexifile *) cf;
+}
+
+
+
+
+/* Implementations of class methods */
+
+static bool 
+impl_get_case(const struct flexifile *ff, unsigned long casenum, 
+	      struct ccase *c)
+{
+  if ( casenum >= ff->case_cnt) 
+    return false;
+
+  case_clone (c, &ff->cases[casenum]);
+  
+  return true;
+}
+
+#if DEBUGGING
+static void
+dumpcasedata(struct ccase *c)
+{
+  int i;
+  for ( i = 0 ; i < c->case_data->value_cnt * MAX_SHORT_STRING; ++i ) 
+    putchar(c->case_data->values->s[i]);
+  putchar('\n');
+}
+#endif
+
+static bool 
+impl_resize (struct flexifile *ff, int n_values, int posn)
+{
+  int i;
+  for( i = 0 ; i < ff->case_cnt ; ++i ) 
+    {
+      struct ccase c;
+      case_create (&c, ff->value_cnt + n_values);
+
+      case_copy (&c, 0, &ff->cases[i], 0, posn);
+      if ( n_values > 0 ) 
+	memset (case_data_rw(&c, posn), ' ', n_values * MAX_SHORT_STRING) ;
+      case_copy (&c, posn + n_values, 
+		 &ff->cases[i], posn, ff->value_cnt - posn);
+
+      case_destroy (&ff->cases[i]);
+      ff->cases[i] = c;
+    }
+
+  ff->value_cnt += n_values;
+
+  return true;
+}
+
+static bool
+impl_insert_case (struct flexifile *ff, struct ccase *c, int posn)
+{
+  int i;
+  struct ccase blank;
+  
+  assert (ff);
+
+  if ( posn > ff->case_cnt )
+    return false;
+
+  if ( posn >= ff->capacity ) 
+    grow(ff);
+
+  case_create(&blank, ff->value_cnt);
+
+  flexifile_append(CASEFILE(ff), &blank);
+
+  case_destroy(&blank);
+
+  /* Shift the existing cases down one */
+  for ( i = ff->case_cnt ; i > posn; --i)
+      case_move(&ff->cases[i], &ff->cases[i-1]);
+
+  case_clone (&ff->cases[posn], c);
+
+  return true;
+}
+
+
+static bool 
+impl_delete_cases (struct flexifile *ff, int n_cases, int first)
+{
+  int i;
+
+  if ( ff->case_cnt < first + n_cases ) 
+    return false;
+
+  for ( i = first ; i < first + n_cases; ++i ) 
+    case_destroy (&ff->cases[i]);
+  
+  /* Shift the cases up by N_CASES */
+  for ( i = first; i < ff->case_cnt - n_cases; ++i ) 
+    {
+      case_move (&ff->cases[i], &ff->cases[i+ n_cases]);
+    }
+
+  ff->case_cnt -= n_cases;
+  
+  return true;
+}
Index: src/data/flexifile.h
===================================================================
RCS file: src/data/flexifile.h
diff -N src/data/flexifile.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ src/data/flexifile.h	13 Jul 2006 05:27:04 -0000
@@ -0,0 +1,52 @@
+/* PSPP - computes sample statistics.
+   Copyright (C) 2006 Free Software Foundation, Inc.
+   Written by John Darrington <john@darrington.wattle.id.au>
+
+   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., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301, USA. */
+
+#ifndef FLEXIFILE_H
+#define FLEXIFILE_H
+
+#include <config.h>
+#include <stdbool.h>
+#include <stdlib.h>
+
+struct ccase;
+struct casefile;
+struct casereader;
+struct flexifile;
+struct flexifilereader;
+
+/* Casts */
+struct flexifile * flexifile_cast(const struct casefile *cf);
+struct flexifilereader *flexifilereader_cast(const struct casereader *cr);
+
+
+#define FLEXIFILE(CF) flexifile_cast(CF)
+#define FLEXIFILEREADER(CR) flexifilereader_cast(CR)
+
+struct casefile *flexifile_create (size_t value_cnt);
+
+bool flexifile_get_case(const struct flexifile *ff, unsigned long casenum, 
+			struct ccase *const c);
+
+bool flexifile_resize (struct flexifile *ff, int n_values, int posn);
+
+bool flexifile_insert_case (struct flexifile *ff, struct ccase *c, int posn);
+bool flexifile_delete_cases (struct flexifile *ff, int n_cases, int first);
+
+
+#endif
Index: src/data/procedure.c
===================================================================
RCS file: /cvsroot/pspp/pspp/src/data/procedure.c,v
retrieving revision 1.9
diff -u -p -r1.9 procedure.c
--- src/data/procedure.c	7 May 2006 05:46:26 -0000	1.9
+++ src/data/procedure.c	13 Jul 2006 05:27:05 -0000
@@ -28,6 +28,7 @@
 #include <data/case-sink.h>
 #include <data/case.h>
 #include <data/casefile.h>
+#include <data/fastfile.h>
 #include <data/dictionary.h>
 #include <data/file-handle-def.h>
 #include <data/procedure.h>
@@ -168,7 +169,7 @@ multipass_procedure (bool (*proc_func) (
   struct multipass_aux_data aux_data;
   bool ok;
 
-  aux_data.casefile = casefile_create (dict_get_next_value_idx (default_dict));
+  aux_data.casefile = fastfile_create (dict_get_next_value_idx (default_dict));
   aux_data.proc_func = proc_func;
   aux_data.aux = aux;
 
@@ -617,7 +618,7 @@ multipass_split_case_func (const struct 
         ok = multipass_split_output (aux);
 
       /* Start a new casefile. */
-      aux->casefile = casefile_create (dict_get_next_value_idx (default_dict));
+      aux->casefile = fastfile_create (dict_get_next_value_idx (default_dict));
     }
 
   return casefile_append (aux->casefile, c) && ok;
Index: src/data/scratch-writer.c
===================================================================
RCS file: /cvsroot/pspp/pspp/src/data/scratch-writer.c,v
retrieving revision 1.2
diff -u -p -r1.2 scratch-writer.c
--- src/data/scratch-writer.c	6 May 2006 22:37:30 -0000	1.2
+++ src/data/scratch-writer.c	13 Jul 2006 05:27:05 -0000
@@ -22,6 +22,7 @@
 #include <stdlib.h>
 #include "case.h"
 #include "casefile.h"
+#include "fastfile.h"
 #include "dictionary.h"
 #include "file-handle-def.h"
 #include "scratch-handle.h"
@@ -72,7 +73,7 @@ scratch_writer_open (struct file_handle 
   /* Create new contents. */
   sh = xmalloc (sizeof *sh);
   sh->dictionary = scratch_dict;
-  sh->casefile = casefile_create (dict_get_next_value_idx (sh->dictionary));
+  sh->casefile = fastfile_create (dict_get_next_value_idx (sh->dictionary));
 
   /* Create writer. */
   writer = xmalloc (sizeof *writer);
@@ -115,7 +116,6 @@ scratch_writer_close (struct scratch_wri
 {
   struct casefile *cf = writer->handle->casefile;
   bool ok = casefile_error (cf);
-  casefile_mode_reader (cf);
   fh_close (writer->fh, "scratch file", "we");
   free (writer);
   return ok;
Index: src/data/storage-stream.c
===================================================================
RCS file: /cvsroot/pspp/pspp/src/data/storage-stream.c,v
retrieving revision 1.5
diff -u -p -r1.5 storage-stream.c
--- src/data/storage-stream.c	7 Jul 2006 02:10:08 -0000	1.5
+++ src/data/storage-stream.c	13 Jul 2006 05:27:05 -0000
@@ -28,6 +28,7 @@
 #include <data/case-source.h>
 #include <data/case.h>
 #include <data/casefile.h> 
+#include <data/fastfile.h> 
 
 #include "xalloc.h"
 
@@ -46,7 +47,7 @@ storage_sink_open (struct case_sink *sin
   struct storage_stream_info *info;
 
   sink->aux = info = xmalloc (sizeof *info);
-  info->casefile = casefile_create (sink->value_cnt);
+  info->casefile = fastfile_create (sink->value_cnt);
 }
 
 /* Destroys storage stream represented by INFO. */
Index: src/language/data-io/get.c
===================================================================
RCS file: /cvsroot/pspp/pspp/src/language/data-io/get.c,v
retrieving revision 1.15
diff -u -p -r1.15 get.c
--- src/language/data-io/get.c	8 Jul 2006 03:05:51 -0000	1.15
+++ src/language/data-io/get.c	13 Jul 2006 05:27:06 -0000
@@ -27,6 +27,7 @@
 #include <data/case-source.h>
 #include <data/case.h>
 #include <data/casefile.h>
+#include <data/fastfile.h>
 #include <data/dictionary.h>
 #include <data/por-file-writer.h>
 #include <data/procedure.h>
@@ -1126,7 +1127,7 @@ cmd_match_files (void)
     discard_variables ();
 
   dict_compact_values (mtf.dict);
-  mtf.output = casefile_create (dict_get_next_value_idx (mtf.dict));
+  mtf.output = fastfile_create (dict_get_next_value_idx (mtf.dict));
   mtf.seq_nums = xcalloc (dict_get_var_cnt (mtf.dict), sizeof *mtf.seq_nums);
   case_create (&mtf.mtf_case, dict_get_next_value_idx (mtf.dict));
 
Index: src/language/tests/casefile-test.c
===================================================================
RCS file: /cvsroot/pspp/pspp/src/language/tests/casefile-test.c,v
retrieving revision 1.3
diff -u -p -r1.3 casefile-test.c
--- src/language/tests/casefile-test.c	7 Jun 2006 01:48:47 -0000	1.3
+++ src/language/tests/casefile-test.c	13 Jul 2006 05:27:06 -0000
@@ -19,6 +19,8 @@
 
 #include <config.h>
 #include <data/casefile.h>
+#include <data/fastfile.h>
+
 #include <data/case.h>
 
 #include <gsl/gsl_randist.h>
@@ -88,7 +90,7 @@ test_casefile (int pattern, size_t value
   size_t i, j;
 
   rng = gsl_rng_alloc (gsl_rng_mt19937);
-  cf = casefile_create (value_cnt);
+  cf = fastfile_create (value_cnt);
   if (pattern == 5)
     casefile_to_disk (cf);
   for (i = 0; i < case_cnt; i++)
@@ -136,24 +138,6 @@ test_casefile (int pattern, size_t value
     casereader_destroy (r1);
   if (pattern != 2)
     casereader_destroy (r2);
-  if (pattern > 3) 
-    {
-      int *order;
-      r1 = casefile_get_random_reader (cf);
-      order = xmalloc (sizeof *order * case_cnt);
-      for (i = 0; i < case_cnt; i++)
-        order[i] = i;
-      if (case_cnt > 0)
-        gsl_ran_shuffle (rng, order, case_cnt, sizeof *order);
-      for (i = 0; i < case_cnt; i++)
-        {
-          int case_idx = order[i];
-          casereader_seek (r1, case_idx);
-          read_and_verify_random_case (cf, r1, case_idx);
-        }
-      casereader_destroy (r1);
-      free (order);
-    }
   if (pattern > 2) 
     {
       r1 = casefile_get_destructive_reader (cf);
Index: src/math/sort.c
===================================================================
RCS file: /cvsroot/pspp/pspp/src/math/sort.c,v
retrieving revision 1.11
diff -u -p -r1.11 sort.c
--- src/math/sort.c	8 Jul 2006 03:05:52 -0000	1.11
+++ src/math/sort.c	13 Jul 2006 05:27:07 -0000
@@ -30,6 +30,7 @@
 #include <data/case-source.h>
 #include <data/case.h>
 #include <data/casefile.h>
+#include <data/fastfile.h>
 #include <data/procedure.h>
 #include <data/settings.h>
 #include <data/variable.h>
@@ -162,7 +163,7 @@ do_internal_sort (struct casereader *rea
     return NULL;
       
   case_cnt = casefile_get_case_cnt (src);
-  dst = casefile_create (casefile_get_value_cnt (src));
+  dst = fastfile_create (casefile_get_value_cnt (src));
   if (case_cnt != 0) 
     {
       struct indexed_case *cases = nmalloc (sizeof *cases, case_cnt);
@@ -508,7 +509,7 @@ start_run (struct initial_run_state *irs
 {
   irs->run++;
   irs->case_cnt = 0;
-  irs->casefile = casefile_create (irs->xsrt->value_cnt);
+  irs->casefile = fastfile_create (irs->xsrt->value_cnt);
   casefile_to_disk (irs->casefile);
   case_nullify (&irs->last_output); 
 }
@@ -673,7 +674,7 @@ merge_once (struct external_sort *xsrt,
     }
 
   /* Create output file. */
-  output = casefile_create (xsrt->value_cnt);
+  output = fastfile_create (xsrt->value_cnt);
   casefile_to_disk (output);
 
   /* Merge. */
Index: src/ui/gui/menu-actions.c
===================================================================
RCS file: /cvsroot/pspp/pspp/src/ui/gui/menu-actions.c,v
retrieving revision 1.12
diff -u -p -r1.12 menu-actions.c
--- src/ui/gui/menu-actions.c	13 Jul 2006 04:00:06 -0000	1.12
+++ src/ui/gui/menu-actions.c	13 Jul 2006 05:27:08 -0000
@@ -196,9 +196,7 @@ load_system_file(const gchar *file_name)
 	  g_warning("Cannot write case to casefile\n");
 	  break;
 	}
-
       case_destroy(&c);
-
     }
   
   sfm_close_reader(reader);      
@@ -345,57 +343,38 @@ on_quit1_activate                      (
 
 
 void
-on_cut1_activate                       (GtkMenuItem     *menuitem,
-                                        gpointer         user_data)
-{
-
-}
-
-
-void
-on_copy1_activate                      (GtkMenuItem     *menuitem,
-                                        gpointer         user_data)
-{
-
-}
-
-
-void
-on_paste1_activate                     (GtkMenuItem     *menuitem,
+on_clear_activate                    (GtkMenuItem     *menuitem,
                                         gpointer         user_data)
 {
+  GtkNotebook *notebook = GTK_NOTEBOOK(get_widget_assert(xml, "notebook1"));
+  gint page = -1;
 
-}
+  page = gtk_notebook_get_current_page(notebook);
 
-/* Fill a case with SYSMIS for numeric and whitespace for string
-   variables respectively */ 
-static gboolean 
-blank_case(struct ccase *cc, gpointer _dict)
+  switch (page) 
 {
-  gint i;
-  PsppireDict *dict = _dict;
-
-  for(i = 0 ; i < psppire_dict_get_var_cnt(dict); ++i ) 
+    case PAGE_DATA_SHEET:
     {
-      union value *val ;
-
-      const struct PsppireVariable *var = psppire_dict_get_variable(dict, i);
-      
-      gint idx = psppire_variable_get_fv(var);
-
-      val = case_data_rw(cc, idx) ;
+	GtkSheet *data_sheet = GTK_SHEET(get_widget_assert(xml, "data_sheet"));
+	PsppireDataStore *data_store = 
+	  PSPPIRE_DATA_STORE(gtk_sheet_get_model(data_sheet));
 
-      if ( psppire_variable_get_type(var) == ALPHA ) 
-	memset(val->s, ' ', psppire_variable_get_width(var));
+	if ( data_sheet->state == GTK_SHEET_ROW_SELECTED )
+	  psppire_case_file_delete_cases(data_store->case_file,
+				       data_sheet->range.rowi 
+					 - data_sheet->range.row0 + 1,
+				       data_sheet->range.row0);
       else
-	val->f = SYSMIS;
-
-      case_unshare(cc);
+	  {
+	    gtk_sheet_cell_clear(data_sheet, 
+				 data_sheet->active_cell.row,
+				 data_sheet->active_cell.col);
     }
-
-  return TRUE;
+      }
+      break;
 }
 
+}
 
 void
 on_insert1_activate                    (GtkMenuItem     *menuitem,
@@ -601,15 +580,22 @@ static GtkNotebook *notebook = 0;
 static void
 switch_menus(gint page)
 {
+  GtkWidget *insert_variable = get_widget_assert(xml, "insert-variable");
+  GtkWidget *insert_cases = get_widget_assert(xml, "insert-cases");
+
   switch (page) 
     {
     case PAGE_VAR_SHEET:
       gtk_widget_hide(menuitems[PAGE_VAR_SHEET]);
       gtk_widget_show(menuitems[PAGE_DATA_SHEET]);
+      gtk_widget_set_sensitive(insert_variable, TRUE);
+      gtk_widget_set_sensitive(insert_cases, FALSE);
       break;
     case PAGE_DATA_SHEET:
       gtk_widget_show(menuitems[PAGE_VAR_SHEET]);
       gtk_widget_hide(menuitems[PAGE_DATA_SHEET]);
+      gtk_widget_set_sensitive(insert_variable, FALSE);
+      gtk_widget_set_sensitive(insert_cases, TRUE);
       break;
     default:
       g_assert_not_reached();
@@ -736,3 +722,44 @@ on_sort_cases_activate (GtkMenuItem     
       break;
     }
 }
+
+
+static void 
+insert_case(void)
+{
+  gint row, col;
+  PsppireDataStore *data_store ;
+  GtkSheet *data_sheet = GTK_SHEET(get_widget_assert(xml, "data_sheet"));
+
+  data_store = PSPPIRE_DATA_STORE(gtk_sheet_get_model(data_sheet));
+
+  gtk_sheet_get_active_cell(data_sheet, &row, &col);
+
+  psppire_case_file_insert_case(data_store->case_file, row);
+}
+
+void
+on_insert_case_clicked (GtkButton *button, gpointer user_data)      
+{
+  insert_case();
+}
+
+void
+on_insert_cases (GtkMenuItem *menuitem, gpointer user_data)
+{
+  insert_case();
+}
+
+
+void
+on_insert_variable (GtkMenuItem *menuitem, gpointer user_data)
+{
+  gint row, col;
+  GtkSheet *var_sheet = GTK_SHEET(get_widget_assert(xml, "variable_sheet"));
+
+  gtk_sheet_get_active_cell(var_sheet, &row, &col);
+
+  psppire_dict_insert_variable(the_dictionary, row, NULL);
+}
+
+
Index: src/ui/gui/psppire-case-file.c
===================================================================
RCS file: /cvsroot/pspp/pspp/src/ui/gui/psppire-case-file.c,v
retrieving revision 1.2
diff -u -p -r1.2 psppire-case-file.c
--- src/ui/gui/psppire-case-file.c	24 Jun 2006 09:08:50 -0000	1.2
+++ src/ui/gui/psppire-case-file.c	13 Jul 2006 05:27:08 -0000
@@ -28,10 +28,11 @@
 #include <gtksheet/gtkextra-marshal.h>
 
 #include <data/case.h>
+#include <data/flexifile.h>
 #include <data/casefile.h>
 #include <data/data-in.h>
-
 #include <math/sort.h>
+#include <libpspp/misc.h>
 
 /* --- prototypes --- */
 static void psppire_case_file_class_init	(PsppireCaseFileClass	*class);
@@ -132,8 +133,8 @@ psppire_case_file_finalize (GObject *obj
 {
   PsppireCaseFile *cf = PSPPIRE_CASE_FILE (object);
   
-  if ( cf->casefile) 
-    casefile_destroy(cf->casefile);
+  if ( cf->flexifile) 
+    casefile_destroy(cf->flexifile);
 
   G_OBJECT_CLASS (parent_class)->finalize (object);
 }
@@ -141,7 +142,7 @@ psppire_case_file_finalize (GObject *obj
 static void
 psppire_case_file_init (PsppireCaseFile *cf)
 {
-  cf->casefile = 0;
+  cf->flexifile = 0;
 }
 
 /**
@@ -151,16 +152,55 @@ psppire_case_file_init (PsppireCaseFile 
  * Creates a new #PsppireCaseFile. 
  */
 PsppireCaseFile*
-psppire_case_file_new (gint var_cnt)
+psppire_case_file_new (gint val_cnt)
 {
   PsppireCaseFile *cf = g_object_new (G_TYPE_PSPPIRE_CASE_FILE, NULL);
 
-  cf->casefile = casefile_create(var_cnt);
+  cf->flexifile = flexifile_create(val_cnt);
 
   return cf;
 }
 
 
+gboolean
+psppire_case_file_delete_cases(PsppireCaseFile *cf, gint n_cases, gint first)
+{
+  int result;
+
+  g_return_val_if_fail(cf, FALSE);
+  g_return_val_if_fail(cf->flexifile, FALSE);
+
+  result =  flexifile_delete_cases(FLEXIFILE(cf->flexifile), n_cases,  first);
+
+  g_signal_emit(cf, signal[CASES_DELETED], 0, n_cases, first);
+
+  return result;
+}
+
+/* Insert a blank case to the case file */
+gboolean
+psppire_case_file_insert_case(PsppireCaseFile *cf, 
+			       gint posn)
+{
+  bool result ;
+  struct ccase cc;
+
+  g_return_val_if_fail(cf, FALSE);
+  g_return_val_if_fail(cf->flexifile, FALSE);
+
+  case_create (&cc, casefile_get_value_cnt(cf->flexifile));
+
+  result = flexifile_insert_case(FLEXIFILE(cf->flexifile), &cc, posn);
+  
+  case_destroy (&cc);
+
+  if ( result ) 
+    g_signal_emit(cf, signal[CASE_INSERTED], 0, posn);
+  else
+    g_warning("Cannot insert case at position %d\n", posn);
+		
+  return result;
+}
 
 /* Append a case to the case file */
 gboolean
@@ -171,11 +211,11 @@ psppire_case_file_append_case(PsppireCas
   gint posn ;
 
   g_return_val_if_fail(cf, FALSE);
-  g_return_val_if_fail(cf->casefile, FALSE);
+  g_return_val_if_fail(cf->flexifile, FALSE);
 
-  posn = casefile_get_case_cnt(cf->casefile);
+  posn = casefile_get_case_cnt(cf->flexifile);
 
-  result = casefile_append(cf->casefile, c);
+  result = casefile_append(cf->flexifile, c);
   
   g_signal_emit(cf, signal[CASE_INSERTED], 0, posn);
 		
@@ -188,10 +228,10 @@ psppire_case_file_get_case_count(const P
 {
   g_return_val_if_fail(cf, FALSE);
   
-  if ( ! cf->casefile) 
+  if ( ! cf->flexifile) 
     return 0;
 
-  return casefile_get_case_cnt(cf->casefile);
+  return casefile_get_case_cnt(cf->flexifile);
 }
 
 /* Return the IDXth value from case CASENUM.
@@ -202,14 +242,16 @@ psppire_case_file_get_value(const Psppir
 {
   const union value *v; 
   struct ccase c;
-  struct casereader *reader =  casefile_get_random_reader (cf->casefile);
 
-  casereader_seek(reader, casenum);
+  g_return_val_if_fail(cf, NULL);
+  g_return_val_if_fail(cf->flexifile, NULL);
+
+  g_return_val_if_fail(idx < casefile_get_value_cnt(cf->flexifile), NULL);
 
-  casereader_read(reader, &c);
+  flexifile_get_case(FLEXIFILE(cf->flexifile), casenum, &c);
 
   v = case_data(&c, idx);
-  casereader_destroy(reader);
+
   case_destroy(&c);
 
   return v;
@@ -218,22 +260,56 @@ psppire_case_file_get_value(const Psppir
 void
 psppire_case_file_clear(PsppireCaseFile *cf)
 {
-  casefile_destroy(cf->casefile);
-  cf->casefile = 0;
+  casefile_destroy(cf->flexifile);
+  cf->flexifile = 0;
   g_signal_emit(cf, signal[CASES_DELETED], 0, 0, -1);
 }
 
-/* Set the IDXth value of case C using FF and DATA */
+/* Set the IDXth value of case C to SYSMIS/EMPTY */
 gboolean
 psppire_case_file_set_value(PsppireCaseFile *cf, gint casenum, gint idx,
+			    union value *v, gint width)
+{
+  struct ccase cc ;
+  int bytes;
+
+  g_return_val_if_fail(cf, FALSE);
+  g_return_val_if_fail(cf->flexifile, FALSE);
+
+  g_return_val_if_fail(idx < casefile_get_value_cnt(cf->flexifile), FALSE);
+
+  if ( ! flexifile_get_case(FLEXIFILE(cf->flexifile), casenum, &cc) )
+    return FALSE;
+
+  if ( width == 0 ) 
+    bytes = MAX_SHORT_STRING;
+  else
+    bytes = DIV_RND_UP(width, MAX_SHORT_STRING) * MAX_SHORT_STRING ;
+
+  /* Cast away const in flagrant abuse of the casefile */
+  memcpy((union value *)case_data(&cc, idx), v, bytes);
+
+  g_signal_emit(cf, signal[CASE_CHANGED], 0, casenum);
+
+  return TRUE;
+}
+
+
+
+/* Set the IDXth value of case C using D_IN */
+gboolean
+psppire_case_file_data_in(PsppireCaseFile *cf, gint casenum, gint idx,
 			    struct data_in *d_in)
 {
   struct ccase cc ;
 
-  struct casereader *reader =  casefile_get_random_reader (cf->casefile);
+  g_return_val_if_fail(cf, FALSE);
+  g_return_val_if_fail(cf->flexifile, FALSE);
+
+  g_return_val_if_fail(idx < casefile_get_value_cnt(cf->flexifile), FALSE);
 
-  casereader_seek(reader, casenum);
-  casereader_read(reader, &cc);
+  if ( ! flexifile_get_case(FLEXIFILE(cf->flexifile), casenum, &cc) )
+    return FALSE;
 
   /* Cast away const in flagrant abuse of the casefile */
   d_in->v = (union value *) case_data(&cc, idx);
@@ -241,9 +317,6 @@ psppire_case_file_set_value(PsppireCaseF
   if ( ! data_in(d_in) ) 
     g_warning("Cant set value\n");
 
-  case_destroy(&cc);
-  casereader_destroy(reader);
-
   g_signal_emit(cf, signal[CASE_CHANGED], 0, casenum);
 
   return TRUE;
@@ -253,12 +326,45 @@ psppire_case_file_set_value(PsppireCaseF
 void
 psppire_case_file_sort(PsppireCaseFile *cf, const struct sort_criteria *sc)
 {
+  struct ccase cc;
   gint c;
-  struct casereader *reader = casefile_get_reader(cf->casefile);
-  cf->casefile = sort_execute(reader, sc);
+  struct casefile *cfile;
+  struct casereader *reader = casefile_get_reader(cf->flexifile);
+  const int value_cnt = casefile_get_value_cnt(cf->flexifile);
+
+  cfile = sort_execute(reader, sc);
+
+  casefile_destroy(cf->flexifile);
+
+  /* Copy casefile into flexifile */
+
+  reader = casefile_get_destructive_reader(cfile);
+  cf->flexifile = flexifile_create(value_cnt);
+  while(casereader_read(reader, &cc))
+      casefile_append(cf->flexifile, &cc);
+  
 
   /* FIXME: Need to have a signal to change a range of cases, instead of
      calling a signal many times */
-  for ( c = 0 ; c < casefile_get_case_cnt(cf->casefile) ; ++c ) 
+  for ( c = 0 ; c < casefile_get_case_cnt(cf->flexifile) ; ++c ) 
     g_signal_emit(cf, signal[CASE_CHANGED], 0, c);
 }
+
+
+/* Resize the cases in the casefile, by inserting N_VALUES into every 
+   one of them. */
+gboolean 
+psppire_case_file_insert_values(PsppireCaseFile *cf, 
+				gint n_values, gint before)
+{
+  g_return_val_if_fail(cf, FALSE);
+
+  if ( ! cf->flexifile ) 
+    {
+      cf->flexifile = flexifile_create(n_values);
+      return TRUE;
+    }
+
+  return flexifile_resize(FLEXIFILE(cf->flexifile), n_values, before);
+}
+
Index: src/ui/gui/psppire-case-file.h
===================================================================
RCS file: /cvsroot/pspp/pspp/src/ui/gui/psppire-case-file.h,v
retrieving revision 1.2
diff -u -p -r1.2 psppire-case-file.h
--- src/ui/gui/psppire-case-file.h	24 Jun 2006 09:08:50 -0000	1.2
+++ src/ui/gui/psppire-case-file.h	13 Jul 2006 05:27:08 -0000
@@ -47,13 +47,13 @@ typedef struct _PsppireCaseFile	   Psppi
 typedef struct _PsppireCaseFileClass PsppireCaseFileClass;
 
 struct ccase;
-struct casefile;
+struct casefilefile;
 
 struct _PsppireCaseFile
 {
   GObject             parent;
 
-  struct casefile *casefile;
+  struct casefile *flexifile;
 };
 
 
@@ -79,11 +79,23 @@ const union value * psppire_case_file_ge
 
 struct data_in;
 
-gboolean psppire_case_file_set_value(PsppireCaseFile *cf, gint c, gint idx,
+gboolean psppire_case_file_data_in(PsppireCaseFile *cf, gint c, gint idx,
 				 struct data_in *d_in);
 
+gboolean psppire_case_file_set_value(PsppireCaseFile *cf, gint casenum, 
+				     gint idx, union value *v, gint width);
+
 void psppire_case_file_clear(PsppireCaseFile *cf);
 
+
+gboolean psppire_case_file_delete_cases(PsppireCaseFile *cf, gint n_rows, 
+					gint first);
+
+gboolean psppire_case_file_insert_case(PsppireCaseFile *cf, gint row);
+
+
+gboolean psppire_case_file_insert_values(PsppireCaseFile *cf, gint n_values, gint before);
+
 struct sort_criteria;
 void psppire_case_file_sort(PsppireCaseFile *cf, const struct sort_criteria *);
 
Index: src/ui/gui/psppire-data-store.c
===================================================================
RCS file: /cvsroot/pspp/pspp/src/ui/gui/psppire-data-store.c,v
retrieving revision 1.14
diff -u -p -r1.14 psppire-data-store.c
--- src/ui/gui/psppire-data-store.c	24 Jun 2006 09:08:50 -0000	1.14
+++ src/ui/gui/psppire-data-store.c	13 Jul 2006 05:27:08 -0000
@@ -268,31 +268,49 @@ static void
 insert_variable_callback(GObject *obj, gint var_num, gpointer data)
 {
   PsppireDataStore *store;
+  gint posn;
 
   g_return_if_fail (data);
 
   store  = PSPPIRE_DATA_STORE(data);
   
-  /* 
-  g_sheet_model_range_changed (G_SHEET_MODEL(store),
-				 casenum, -1,
-				 psppire_case_array_get_n_cases(store->cases),
-				 -1);
-  */
+  if ( var_num > 0 ) 
+    {
+      struct PsppireVariable *variable;
+      variable = psppire_dict_get_variable(store->dict, var_num);
 
-#if 0
-  psppire_case_array_resize(store->cases, 
-			 dict_get_next_value_idx (store->dict->dict));
-#endif
+      posn = psppire_variable_get_fv(variable);
+    }
+  else
+    {
+      posn = 0;
+    }
+
+  psppire_case_file_insert_values(store->case_file, 1, posn);
 
   g_sheet_column_columns_changed(G_SHEET_COLUMN(store),
 				  var_num, 1);
 
-
   g_sheet_model_columns_inserted (G_SHEET_MODEL(store), var_num, 1);
 }
 
 
+static void
+dict_size_change_callback(GObject *obj, 
+			  gint posn, gint adjustment, gpointer data)
+{
+  PsppireDataStore *store ;
+
+  g_return_if_fail (data);
+
+  store  = PSPPIRE_DATA_STORE(data);
+
+  /* 
+  if ( adjustment > 0 )
+  */
+  psppire_case_file_insert_values (store->case_file, adjustment, posn);
+}
+
 
 
 /**
@@ -356,7 +374,6 @@ psppire_data_store_set_dictionary(Psppir
 		   G_CALLBACK(changed_case_callback), 
 		   data_store);
 
-
   g_signal_connect(dict, "variable-inserted", 
 		   G_CALLBACK(insert_variable_callback), 
 		   data_store);
@@ -365,6 +382,10 @@ psppire_data_store_set_dictionary(Psppir
 		   G_CALLBACK(delete_variables_callback), 
 		   data_store);
 
+  g_signal_connect (dict, "dict-size-changed", 
+		    G_CALLBACK(dict_size_change_callback),
+		    data_store);
+
   /* The entire model has changed */
   g_sheet_model_range_changed (G_SHEET_MODEL(data_store), -1, -1, -1, -1);
   
@@ -406,6 +427,8 @@ psppire_data_store_get_string(const GShe
 
   v = psppire_case_file_get_value(store->case_file, row, idx);
 
+  g_return_val_if_fail(v, NULL);
+
   if ( store->show_labels) 
     {
       const struct val_labs * vl = psppire_variable_get_value_labels(pv);
@@ -455,8 +478,8 @@ psppire_data_store_clear_datum(GSheetMod
   else
     memcpy(v.s, "", MAX_SHORT_STRING);
 
-  psppire_case_file_set_value(store->case_file, row, index, &v);
-
+  psppire_case_file_set_value(store->case_file, row, index, &v, 
+			      psppire_variable_get_width(pv));
   return TRUE;
 }
 
@@ -500,15 +523,7 @@ psppire_data_store_set_string(GSheetMode
     d_in.format = * psppire_variable_get_write_spec(pv);
     d_in.flags = 0;
 
-    /* 
-    if ( ! data_in(&d_in) ) 
-      {
-	g_warning("Cannot encode string");
-	return FALSE;
-      }
-    */
-
-    psppire_case_file_set_value(store->case_file, row, index, &d_in) ;
+    psppire_case_file_data_in(store->case_file, row, index, &d_in) ;
   }
 
   return TRUE;
@@ -541,19 +556,6 @@ psppire_data_store_show_labels(PsppireDa
 
 
 
-static gboolean 
-write_case(const struct ccase *cc, 
-	   gpointer aux)
-{
-  struct sfm_writer *writer = aux;
-
-  if ( ! sfm_write_case(writer, cc) )
-    return FALSE;
-
-
-  return TRUE;
-}
-
 void
 psppire_data_store_create_system_file(PsppireDataStore *store,
 			      struct file_handle *handle)
Index: src/ui/gui/psppire-dict.c
===================================================================
RCS file: /cvsroot/pspp/pspp/src/ui/gui/psppire-dict.c,v
retrieving revision 1.8
diff -u -p -r1.8 psppire-dict.c
--- src/ui/gui/psppire-dict.c	24 Jun 2006 09:08:50 -0000	1.8
+++ src/ui/gui/psppire-dict.c	13 Jul 2006 05:27:09 -0000
@@ -48,6 +48,7 @@ static void dictionary_tree_model_init(G
 static GObjectClass     *parent_class = NULL;
 
 enum  {VARIABLE_CHANGED, 
+       VARIABLE_RESIZED,
        VARIABLE_INSERTED,
        VARIABLES_DELETED, 
        n_SIGNALS};
@@ -146,6 +147,19 @@ psppire_dict_class_init (PsppireDictClas
 		  G_TYPE_INT,
 		  G_TYPE_INT);
 
+
+  signal[VARIABLE_RESIZED] =
+    g_signal_new ("dict-size-changed",
+		  G_TYPE_FROM_CLASS(class),
+		  G_SIGNAL_RUN_FIRST,
+		  0,
+		  NULL, NULL,
+		  gtkextra_VOID__INT_INT,
+		  G_TYPE_NONE, 
+		  2,
+		  G_TYPE_INT,
+		  G_TYPE_INT);
+
 }
 
 static void
@@ -446,6 +460,31 @@ psppire_dict_get_next_value_idx (const P
 }
 
 
+void 
+psppire_dict_resize_variable(PsppireDict *d, const struct PsppireVariable *pv,
+			     gint old_size, gint new_size)
+{
+  gint fv;
+  g_return_if_fail (d);
+  g_return_if_fail (d->dict);
+  
+  if ( old_size == new_size ) 
+    return ;
+
+  pv->v->nv = new_size;
+
+  dict_compact_values(d->dict);
+
+  fv = psppire_variable_get_fv(pv);
+
+  g_signal_emit(d, signal[VARIABLE_RESIZED], 0, 
+		fv + old_size, 
+		new_size - old_size );  
+}
+
+
+
+
 
 /* Tree Model Stuff */
 
Index: src/ui/gui/psppire-dict.h
===================================================================
RCS file: /cvsroot/pspp/pspp/src/ui/gui/psppire-dict.h,v
retrieving revision 1.5
diff -u -p -r1.5 psppire-dict.h
--- src/ui/gui/psppire-dict.h	24 Jun 2006 09:08:50 -0000	1.5
+++ src/ui/gui/psppire-dict.h	13 Jul 2006 05:27:09 -0000
@@ -102,6 +102,10 @@ void psppire_dict_delete_variables(Psppi
 /* Insert a new variable at posn IDX */
 void psppire_dict_insert_variable(PsppireDict *d, gint idx, const gchar *name);
 
+void psppire_dict_resize_variable(PsppireDict *d, 
+				  const struct PsppireVariable *pv,
+				  gint old_size, gint new_size);
+
 gboolean psppire_dict_check_name(const PsppireDict *dict, 
 			      const gchar *name, gboolean report);
 
Index: src/ui/gui/psppire-variable.c
===================================================================
RCS file: /cvsroot/pspp/pspp/src/ui/gui/psppire-variable.c,v
retrieving revision 1.5
diff -u -p -r1.5 psppire-variable.c
--- src/ui/gui/psppire-variable.c	24 Jun 2006 09:08:50 -0000	1.5
+++ src/ui/gui/psppire-variable.c	13 Jul 2006 05:27:09 -0000
@@ -25,6 +25,8 @@
 #include <data/value-labels.h>
 #include <data/format.h>
 
+#include <libpspp/misc.h>
+
 #include "psppire-variable.h"
 #include "psppire-dict.h"
 
@@ -115,8 +117,21 @@ psppire_variable_set_width(struct Psppir
   fmt.w = width;
 
   if ( pv->v->type == ALPHA ) 
+    {
+      gint old_var_cnt , new_var_cnt ;
+
+      if ( pv->v->width == 0 ) 
+	old_var_cnt = 1;
+      else
+	old_var_cnt = DIV_RND_UP(pv->v->width, MAX_SHORT_STRING);
+      
+      new_var_cnt = DIV_RND_UP(width, MAX_SHORT_STRING);
     pv->v->width = width;
 
+      psppire_dict_resize_variable(pv->dict, pv,
+				   old_var_cnt, new_var_cnt);
+    }
+
   return psppire_variable_set_format(pv, &fmt);
 }
 
@@ -124,15 +139,30 @@ psppire_variable_set_width(struct Psppir
 gboolean
 psppire_variable_set_type(struct PsppireVariable *pv, int type)
 {
+  gint old_var_cnt , new_var_cnt ;
+
   g_return_val_if_fail(pv, FALSE);
   g_return_val_if_fail(pv->dict, FALSE);
   g_return_val_if_fail(pv->v, FALSE);
 
   pv->v->type = type; 
 
+  if ( pv->v->width == 0 ) 
+    old_var_cnt = 1;
+  else
+    old_var_cnt = DIV_RND_UP(pv->v->width, MAX_SHORT_STRING);
+
   if ( type == NUMERIC ) 
     pv->v->width = 0;
 
+  if ( pv->v->width == 0 ) 
+    new_var_cnt = 1;
+  else
+    new_var_cnt = DIV_RND_UP(pv->v->width, MAX_SHORT_STRING);
+
+  psppire_dict_resize_variable(pv->dict, pv,
+			       old_var_cnt, new_var_cnt);
+
   psppire_dict_var_changed(pv->dict, pv->v->index);
   return TRUE;
 }
Index: src/ui/gui/psppire.c
===================================================================
RCS file: /cvsroot/pspp/pspp/src/ui/gui/psppire.c,v
retrieving revision 1.15
diff -u -p -r1.15 psppire.c
--- src/ui/gui/psppire.c	4 Jul 2006 01:24:59 -0000	1.15
+++ src/ui/gui/psppire.c	13 Jul 2006 05:27:09 -0000
@@ -110,6 +110,7 @@ main(int argc, char *argv[]) 
   glade_init();
 
 
+
   settings_init();
 
   /* 
Index: src/ui/gui/psppire.glade
===================================================================
RCS file: /cvsroot/pspp/pspp/src/ui/gui/psppire.glade,v
retrieving revision 1.5
diff -u -p -r1.5 psppire.glade
--- src/ui/gui/psppire.glade	24 Jun 2006 09:08:50 -0000	1.5
+++ src/ui/gui/psppire.glade	13 Jul 2006 05:27:11 -0000
@@ -109,45 +109,73 @@
 		  <child>
 		    <widget class="GtkImageMenuItem" id="cut1">
 		      <property name="visible">True</property>
+		      <property name="sensitive">False</property>
 		      <property name="label">gtk-cut</property>
 		      <property name="use_stock">True</property>
-		      <signal name="activate" handler="on_cut1_activate" last_modification_time="Tue, 23 Mar 2004 10:22:51 GMT"/>
 		    </widget>
 		  </child>
 
 		  <child>
 		    <widget class="GtkImageMenuItem" id="copy1">
 		      <property name="visible">True</property>
+		      <property name="sensitive">False</property>
 		      <property name="label">gtk-copy</property>
 		      <property name="use_stock">True</property>
-		      <signal name="activate" handler="on_copy1_activate" last_modification_time="Tue, 23 Mar 2004 10:22:51 GMT"/>
 		    </widget>
 		  </child>
 
 		  <child>
 		    <widget class="GtkImageMenuItem" id="paste1">
 		      <property name="visible">True</property>
+		      <property name="sensitive">False</property>
 		      <property name="label">gtk-paste</property>
 		      <property name="use_stock">True</property>
-		      <signal name="activate" handler="on_paste1_activate" last_modification_time="Tue, 23 Mar 2004 10:22:51 GMT"/>
 		    </widget>
 		  </child>
 
 		  <child>
-		    <widget class="GtkImageMenuItem" id="delete1">
+		    <widget class="GtkMenuItem" id="paste_variables1">
 		      <property name="visible">True</property>
-		      <property name="label">gtk-delete</property>
-		      <property name="use_stock">True</property>
-		      <signal name="activate" handler="on_delete1_activate" last_modification_time="Tue, 23 Mar 2004 10:22:51 GMT"/>
+		      <property name="sensitive">False</property>
+		      <property name="label" translatable="yes">Paste _Variables</property>
+		      <property name="use_underline">True</property>
 		    </widget>
 		  </child>
 
 		  <child>
-		    <widget class="GtkMenuItem" id="insert1">
+		    <widget class="GtkMenuItem" id="clear1">
 		      <property name="visible">True</property>
-		      <property name="label" translatable="yes">_Insert</property>
+		      <property name="label" translatable="yes">Cl_ear</property>
 		      <property name="use_underline">True</property>
-		      <signal name="activate" handler="on_insert1_activate" last_modification_time="Mon, 02 Jan 2006 12:46:02 GMT"/>
+		      <signal name="activate" handler="on_clear_activate" last_modification_time="Sun, 02 Jul 2006 07:00:12 GMT"/>
+		      <accelerator key="Delete" modifiers="0" signal="activate"/>
+		    </widget>
+		  </child>
+
+		  <child>
+		    <widget class="GtkSeparatorMenuItem" id="separator6">
+		      <property name="visible">True</property>
+		    </widget>
+		  </child>
+
+		  <child>
+		    <widget class="GtkImageMenuItem" id="find1">
+		      <property name="visible">True</property>
+		      <property name="sensitive">False</property>
+		      <property name="label" translatable="yes">_Find</property>
+		      <property name="use_underline">True</property>
+
+		      <child internal-child="image">
+			<widget class="GtkImage" id="image10">
+			  <property name="visible">True</property>
+			  <property name="stock">gtk-find</property>
+			  <property name="icon_size">1</property>
+			  <property name="xalign">0.5</property>
+			  <property name="yalign">0.5</property>
+			  <property name="xpad">0</property>
+			  <property name="ypad">0</property>
+			</widget>
+		      </child>
 		    </widget>
 		  </child>
 		</widget>
@@ -254,20 +282,21 @@
 		<widget class="GtkMenu" id="menuitem4_menu">
 
 		  <child>
-		    <widget class="GtkMenuItem" id="insert_variable1">
+		    <widget class="GtkMenuItem" id="insert-variable">
 		      <property name="visible">True</property>
 		      <property name="sensitive">False</property>
 		      <property name="label" translatable="yes">Insert Variable</property>
 		      <property name="use_underline">True</property>
+		      <signal name="activate" handler="on_insert_variable" />
 		    </widget>
 		  </child>
 
 		  <child>
-		    <widget class="GtkMenuItem" id="insert_cases1">
+		    <widget class="GtkMenuItem" id="insert-cases">
 		      <property name="visible">True</property>
-		      <property name="sensitive">False</property>
 		      <property name="label" translatable="yes">Insert Cases</property>
 		      <property name="use_underline">True</property>
+		      <signal name="activate" handler="on_insert_cases" last_modification_time="Sat, 01 Jul 2006 08:40:06 GMT"/>
 		    </widget>
 		  </child>
 
@@ -279,7 +308,7 @@
 		      <signal name="activate" handler="on_go_to_case_activate" last_modification_time="Mon, 19 Jun 2006 10:29:37 GMT"/>
 
 		      <child internal-child="image">
-			<widget class="GtkImage" id="image1">
+			<widget class="GtkImage" id="image11">
 			  <property name="visible">True</property>
 			  <property name="stock">gtk-jump-to</property>
 			  <property name="icon_size">1</property>
@@ -609,6 +638,7 @@
 		  <property name="visible_horizontal">True</property>
 		  <property name="visible_vertical">True</property>
 		  <property name="is_important">False</property>
+		  <signal name="clicked" handler="on_insert_case_clicked" last_modification_time="Sat, 01 Jul 2006 08:41:07 GMT"/>
 		</widget>
 		<packing>
 		  <property name="expand">False</property>
