Fix bug #15766 (/KEEP subcommand on SAVE doesn't fully support ALL)
authorBen Pfaff <blp@gnu.org>
Wed, 5 Jul 2006 02:52:35 +0000 (02:52 +0000)
committerBen Pfaff <blp@gnu.org>
Wed, 5 Jul 2006 02:52:35 +0000 (02:52 +0000)
and additional underlying system file issues.

Reviewed by John Darrington

src/data/ChangeLog
src/data/sys-file-reader.c
src/language/lexer/ChangeLog
src/language/lexer/variable-parser.c
tests/ChangeLog
tests/automake.mk
tests/bugs/keep-all.sh [new file with mode: 0755]

index 8683f041c2187d5b53aa7f3931cf9598e8ab9e63..ac086a7878bd56ff3017be6b83c485b6e6a9fe5d 100644 (file)
@@ -1,3 +1,34 @@
+Tue Jul  4 08:47:35 2006  Ben Pfaff  <blp@gnu.org>
+
+       Fix bug #15766 (/KEEP subcommand on SAVE doesn't fully support
+       ALL) and additional underlying system file issues.
+
+       Thanks to John Darrington for review.
+
+       First problem: var_hash points to variables not owned by the
+       sys-file-reader, which the caller may free or modify.  Use an
+       array of sfm_vars instead, as done earlier (e.g. CVS version
+       1.12).
+       
+       * sys-file-reader.c (struct sfm_reader): Remove var_hash, svars
+       members and remove all code that references it.  Add vars, var_cnt
+       members.  Remove fix_specials member, which was unused.
+       (struct sfm_var) Remove name member, which was unused.
+       (sfm_close_reader) Free vars member instead of var_hash.
+       (compare_var_shortnames) Removed.
+       (hash_var_shortname) Removed.
+       (sfm_open_reader) Fill out vars array.
+       (compare_var_index) Removed.
+       (sfm_read_case) Use vars instead of var_hash.
+       
+       Second problem: we're confused about when we actually have very
+       long strings, causing us to choose incorrectly between slow path
+       and fast path in sfm_read_case.
+
+       * sys-file-reader.c: (sfm_open_reader) Only mark has_vls if we
+       have very long strings, not when we have long variable names,
+       which is an unrelated feature.
+
 Tue Jun 27 12:06:49 2006  Ben Pfaff  <blp@gnu.org>
 
        * variable.h: Move var_set and variable parsing declarations to
 Tue Jun 27 12:06:49 2006  Ben Pfaff  <blp@gnu.org>
 
        * variable.h: Move var_set and variable parsing declarations to
index 98e1a0621d9b5d9be3b6166ab4eb7702d1eb03a3..52a14a45d2e26021d05f9ba32bb20cda5b2f7445 100644 (file)
@@ -55,7 +55,6 @@ struct sfm_reader
   FILE *file;                  /* File stream. */
 
   int reverse_endian;          /* 1=file has endianness opposite us. */
   FILE *file;                  /* File stream. */
 
   int reverse_endian;          /* 1=file has endianness opposite us. */
-  int fix_specials;             /* 1=SYSMIS/HIGHEST/LOWEST differs from us. */
   int value_cnt;               /* Number of `union values's per case. */
   long case_cnt;               /* Number of cases, -1 if unknown. */
   int compressed;              /* 1=compressed, 0=not compressed. */
   int value_cnt;               /* Number of `union values's per case. */
   long case_cnt;               /* Number of cases, -1 if unknown. */
   int compressed;              /* 1=compressed, 0=not compressed. */
@@ -65,8 +64,8 @@ struct sfm_reader
   bool has_vls;         /* True if the file has one or more Very Long Strings*/
 
   /* Variables. */
   bool has_vls;         /* True if the file has one or more Very Long Strings*/
 
   /* Variables. */
-  struct hsh_table *var_hash;
-  struct variable **svars;
+  struct sfm_var *vars;
+  size_t var_cnt;
 
   /* File's special constants. */
   flt64 sysmis;
 
   /* File's special constants. */
   flt64 sysmis;
@@ -86,7 +85,6 @@ struct sfm_reader
 /* A variable in a system file. */
 struct sfm_var 
 {
 /* A variable in a system file. */
 struct sfm_var 
 {
-  char name[SHORT_NAME_LEN + 1];  /* name */
   int width;                  /* 0=numeric, otherwise string width. */
   int fv;                     /* Index into case. */
 };
   int width;                  /* 0=numeric, otherwise string width. */
   int fv;                     /* Index into case. */
 };
@@ -166,7 +164,7 @@ sfm_close_reader (struct sfm_reader *r)
   if (r->fh != NULL)
     fh_close (r->fh, "system file", "rs");
 
   if (r->fh != NULL)
     fh_close (r->fh, "system file", "rs");
 
-  hsh_destroy(r->var_hash);
+  free (r->vars);
   free (r->buf);
   free (r);
 }
   free (r->buf);
   free (r);
 }
@@ -272,59 +270,6 @@ pair_sn_free(void *p, void *aux UNUSED)
 
 
 
 
 
 
-/* A hsh_compare_func that orders variables A and B by their
-   names. */
-static int
-compare_var_shortnames (const void *a_, const void *b_, void *foo UNUSED) 
-{
-  int i;
-  const struct variable *a = a_;
-  const struct variable *b = b_;
-
-  char buf1[SHORT_NAME_LEN + 1];
-  char buf2[SHORT_NAME_LEN + 1];
-
-  memset(buf1, 0, SHORT_NAME_LEN + 1);
-  memset(buf2, 0, SHORT_NAME_LEN + 1);
-
-  for (i = 0 ; i <= SHORT_NAME_LEN ; ++i ) 
-    {
-      buf1[i] = a->short_name[i];
-      if ( '\0' == buf1[i]) 
-       break;
-    }
-
-  for (i = 0 ; i <= SHORT_NAME_LEN ; ++i ) 
-    {
-      buf2[i] = b->short_name[i];
-      if ( '\0' == buf2[i]) 
-       break;
-    }
-
-  return strncmp(buf1, buf2, SHORT_NAME_LEN);
-}
-
-/* A hsh_hash_func that hashes variable V based on its name. */
-static unsigned
-hash_var_shortname (const void *v_, void *foo UNUSED) 
-{
-  int i;
-  const struct variable *v = v_;
-  char buf[SHORT_NAME_LEN + 1];
-
-  memset(buf, 0, SHORT_NAME_LEN + 1); 
-  for (i = 0 ; i <= SHORT_NAME_LEN ; ++i ) 
-    {
-      buf[i] = v->short_name[i];
-      if ( '\0' == buf[i]) 
-       break;
-    }
-
-  return hsh_hash_bytes(buf, strlen(buf));
-}
-
-
-
 /* Opens the system file designated by file handle FH for
    reading.  Reads the system file's dictionary into *DICT.
    If INFO is non-null, then it receives additional info about the
 /* Opens the system file designated by file handle FH for
    reading.  Reads the system file's dictionary into *DICT.
    If INFO is non-null, then it receives additional info about the
@@ -342,7 +287,6 @@ sfm_open_reader (struct file_handle *fh, struct dictionary **dict,
   /* A hash table of long variable names indexed by short name */
   struct hsh_table *short_to_long = NULL;
 
   /* A hash table of long variable names indexed by short name */
   struct hsh_table *short_to_long = NULL;
 
-
   *dict = dict_create ();
   if (!fh_open (fh, FH_REF_FILE, "system file", "rs"))
     goto error;
   *dict = dict_create ();
   if (!fh_open (fh, FH_REF_FILE, "system file", "rs"))
     goto error;
@@ -353,7 +297,6 @@ sfm_open_reader (struct file_handle *fh, struct dictionary **dict,
   r->file = fn_open (fh_get_file_name (fh), "rb");
 
   r->reverse_endian = 0;
   r->file = fn_open (fh_get_file_name (fh), "rb");
 
   r->reverse_endian = 0;
-  r->fix_specials = 0;
   r->value_cnt = 0;
   r->case_cnt = 0;
   r->compressed = 0;
   r->value_cnt = 0;
   r->case_cnt = 0;
   r->compressed = 0;
@@ -361,9 +304,8 @@ sfm_open_reader (struct file_handle *fh, struct dictionary **dict,
   r->weight_idx = -1;
   r->ok = true;
   r->has_vls = false;
   r->weight_idx = -1;
   r->ok = true;
   r->has_vls = false;
-  r->svars = 0;
 
 
-  r->var_hash = hsh_create(4, compare_var_shortnames, hash_var_shortname, 0, 0);
+  r->vars = NULL;
 
   r->sysmis = -FLT64_MAX;
   r->highest = FLT64_MAX;
 
   r->sysmis = -FLT64_MAX;
   r->highest = FLT64_MAX;
@@ -534,8 +476,6 @@ sfm_open_reader (struct file_handle *fh, struct dictionary **dict,
                  char *short_name, *save_ptr;
                   int idx;
 
                  char *short_name, *save_ptr;
                   int idx;
 
-                 r->has_vls = true;
-
                   /* Read data. */
                   subrec14data = xmalloc (bytes + 1);
                  if (!buf_read (r, subrec14data, bytes, 0)) 
                   /* Read data. */
                   subrec14data = xmalloc (bytes + 1);
                  if (!buf_read (r, subrec14data, bytes, 0)) 
@@ -611,7 +551,7 @@ sfm_open_reader (struct file_handle *fh, struct dictionary **dict,
         records have been processed. --- JMD 27 April 2006
       */
                      
         records have been processed. --- JMD 27 April 2006
       */
                      
-                      /* For compatability, make sure dictionary
+                      /* For compatibility, make sure dictionary
                          is in long variable name map order.  In
                          the common case, this has no effect,
                          because the dictionary and the long
                          is in long variable name map order.  In
                          the common case, this has no effect,
                          because the dictionary and the long
@@ -639,6 +579,7 @@ sfm_open_reader (struct file_handle *fh, struct dictionary **dict,
                     }
                  buffer[bytes] = '\0';
 
                     }
                  buffer[bytes] = '\0';
 
+                 r->has_vls = true;
 
                  /* Note:  SPSS v13 terminates this record with 00,
                     whereas SPSS v14 terminates it with 00 09. We must
 
                  /* Note:  SPSS v13 terminates this record with 00,
                     whereas SPSS v14 terminates it with 00 09. We must
@@ -703,8 +644,6 @@ sfm_open_reader (struct file_handle *fh, struct dictionary **dict,
                                  else
                                    l -= v_next->width;
 
                                  else
                                    l -= v_next->width;
 
-                                 hsh_delete(r->var_hash, v_next);
-
                                  dict_delete_var(*dict, v_next);
                                }
 
                                  dict_delete_var(*dict, v_next);
                                }
 
@@ -771,6 +710,24 @@ sfm_open_reader (struct file_handle *fh, struct dictionary **dict,
  success:
   /* Come here on successful completion. */
 
  success:
   /* Come here on successful completion. */
 
+  /* Create an index of dictionary variable widths for
+     sfm_read_case to use.  We cannot use the `struct variables'
+     from the dictionary we created, because the caller owns the
+     dictionary and may destroy or modify its variables. */
+  {
+    size_t i;
+
+    r->var_cnt = dict_get_var_cnt (*dict);
+    r->vars = xnmalloc (r->var_cnt, sizeof *r->vars);
+    for (i = 0; i < r->var_cnt; i++) 
+      {
+        struct variable *v = dict_get_var (*dict, i);
+        struct sfm_var *sv = &r->vars[i];
+        sv->width = v->width;
+        sv->fv = v->fv; 
+      }
+  }
+
   free (var_by_idx);
   hsh_destroy(short_to_long);
   free (subrec14data);
   free (var_by_idx);
   hsh_destroy(short_to_long);
   free (subrec14data);
@@ -1223,9 +1180,6 @@ read_variables (struct sfm_reader *r,
       if (!parse_format_spec (r, sv.print, &vv->print, vv)
          || !parse_format_spec (r, sv.write, &vv->write, vv))
        goto error;
       if (!parse_format_spec (r, sv.print, &vv->print, vv)
          || !parse_format_spec (r, sv.write, &vv->write, vv))
        goto error;
-
-      if ( vv->width != -1)
-       hsh_insert(r->var_hash, vv);
     }
 
   /* Some consistency checks. */
     }
 
   /* Some consistency checks. */
@@ -1679,20 +1633,6 @@ read_compressed_data (struct sfm_reader *r, flt64 *buf)
   return 0;
 }
 
   return 0;
 }
 
-
-static int
-compare_var_index(const void *_v1, const void *_v2, void *aux UNUSED)
-{
-  const struct variable *const *v1 = _v1;
-  const struct variable *const *v2 = _v2;
-
-  if ( (*v1)->index < (*v2)->index) 
-    return -1;
-
-  return ( (*v1)->index > (*v2)->index) ;
-}
-
-
 /* Reads one case from READER's file into C.  Returns nonzero
    only if successful. */
 int
 /* Reads one case from READER's file into C.  Returns nonzero
    only if successful. */
 int
@@ -1701,13 +1641,6 @@ sfm_read_case (struct sfm_reader *r, struct ccase *c)
   if (!r->ok)
     return 0;
 
   if (!r->ok)
     return 0;
 
-  if ( ! r->svars ) 
-    {
-      r->svars = (struct variable **) hsh_data(r->var_hash);
-      sort(r->svars, hsh_count(r->var_hash), 
-          sizeof(*r->svars), compare_var_index, 0);
-    }
-
   if (!r->compressed && sizeof (flt64) == sizeof (double) && ! r->has_vls) 
     {
       /* Fast path: external and internal representations are the
   if (!r->compressed && sizeof (flt64) == sizeof (double) && ! r->has_vls) 
     {
       /* Fast path: external and internal representations are the
@@ -1723,12 +1656,9 @@ sfm_read_case (struct sfm_reader *r, struct ccase *c)
         {
           int i;
           
         {
           int i;
           
-          for (i = 0; i < hsh_count(r->var_hash); i++) 
-           {
-             struct variable *v = r->svars[i];
-             if (v->width == 0)
-               bswap_flt64 (&case_data_rw (c, v->fv)->f);
-           }
+          for (i = 0; i < r->var_cnt; i++) 
+            if (r->vars[i].width == 0)
+              bswap_flt64 (&case_data_rw (c, r->vars[i].fv)->f);
         }
 
       /* Fix up SYSMIS values if needed.
         }
 
       /* Fix up SYSMIS values if needed.
@@ -1737,12 +1667,10 @@ sfm_read_case (struct sfm_reader *r, struct ccase *c)
       if (r->sysmis != SYSMIS) 
         {
           int i;
       if (r->sysmis != SYSMIS) 
         {
           int i;
-          for (i = 0; i < hsh_count(r->var_hash); i++) 
-           {
-             struct variable *v = r->svars[i];
-             if (v->width == 0 && case_num (c, i) == r->sysmis)
-               case_data_rw (c, v->fv)->f = SYSMIS;
-           }
+          
+          for (i = 0; i < r->var_cnt; i++) 
+            if (r->vars[i].width == 0 && case_num (c, i) == r->sysmis)
+              case_data_rw (c, r->vars[i].fv)->f = SYSMIS;
         }
     }
   else 
         }
     }
   else 
@@ -1770,31 +1698,31 @@ sfm_read_case (struct sfm_reader *r, struct ccase *c)
           return 0;
         }
 
           return 0;
         }
 
-      for (i = 0; i < hsh_count(r->var_hash); i++)
+      for (i = 0; i < r->var_cnt; i++)
         {
         {
-         struct variable *tv = r->svars[i];
+         struct sfm_var *sv = &r->vars[i];
 
 
-          if (tv->width == 0)
+          if (sv->width == 0)
             {
               flt64 f = *bounce_cur++;
               if (r->reverse_endian)
                 bswap_flt64 (&f);
             {
               flt64 f = *bounce_cur++;
               if (r->reverse_endian)
                 bswap_flt64 (&f);
-              case_data_rw (c, tv->fv)->f = f == r->sysmis ? SYSMIS : f;
+              case_data_rw (c, sv->fv)->f = f == r->sysmis ? SYSMIS : f;
             }
             }
-          else if (tv->width != -1)
+          else
             {
              flt64 *bc_start = bounce_cur;
              int ofs = 0;
             {
              flt64 *bc_start = bounce_cur;
              int ofs = 0;
-              while (ofs < tv->width )
+              while (ofs < sv->width )
                 {
                 {
-                  const int chunk = MIN (MAX_LONG_STRING, tv->width - ofs);
-                  memcpy (case_data_rw (c, tv->fv)->s + ofs, bounce_cur, chunk);
+                  const int chunk = MIN (MAX_LONG_STRING, sv->width - ofs);
+                  memcpy (case_data_rw (c, sv->fv)->s + ofs, bounce_cur, chunk);
 
                   bounce_cur += DIV_RND_UP (chunk, sizeof (flt64));
 
                   ofs += chunk;
                 }
 
                   bounce_cur += DIV_RND_UP (chunk, sizeof (flt64));
 
                   ofs += chunk;
                 }
-             bounce_cur = bc_start + width_to_bytes(tv->width) / sizeof(flt64);
+             bounce_cur = bc_start + width_to_bytes(sv->width) / sizeof(flt64);
             }
         }
 
             }
         }
 
index 326e859ba3e58b210eb276980c4db2258fbff18a..fbab8e4bb140f3e7cec3758a11de0b570b97bdf2 100644 (file)
@@ -1,3 +1,13 @@
+Tue Jul  4 09:45:12 2006  Ben Pfaff  <blp@gnu.org>
+
+       Fix bug #15766 (/KEEP subcommand on SAVE doesn't fully support
+       ALL) and additional underlying system file issues.
+       
+       * variable-parser.c (add_variable): Move test earlier for clarity
+       and efficiency.
+       (parse_var_set_vars) Accept ALL within a variable list, not just
+       at the beginning of one.
+
 Tue Jun 27 22:54:30 2006  Ben Pfaff  <blp@gnu.org>
 
        * automake.mk (src_language_lexer_liblexer_a_SOURCES): Add
 Tue Jun 27 22:54:30 2006  Ben Pfaff  <blp@gnu.org>
 
        * automake.mk (src_language_lexer_liblexer_a_SOURCES): Add
index 7f25d8eb685f0be26818df389bb2fef07182db38..904ebe755f43104b92c9267fd49a13ac7c6395a6 100644 (file)
@@ -164,20 +164,16 @@ add_variable (struct variable ***v, size_t *nv, size_t *mv,
          (*v)[0]->name, add->name, add->name);
   else if ((pv_opts & PV_NO_DUPLICATE) && included[idx]) 
     msg (SE, _("Variable %s appears twice in variable list."), add->name);
          (*v)[0]->name, add->name, add->name);
   else if ((pv_opts & PV_NO_DUPLICATE) && included[idx]) 
     msg (SE, _("Variable %s appears twice in variable list."), add->name);
-  else 
+  else if ((pv_opts & PV_DUPLICATE) || !included[idx])
     {
       if (*nv >= *mv)
         {
           *mv = 2 * (*nv + 1);
           *v = xnrealloc (*v, *mv, sizeof **v);
         }
     {
       if (*nv >= *mv)
         {
           *mv = 2 * (*nv + 1);
           *v = xnrealloc (*v, *mv, sizeof **v);
         }
-
-      if ((pv_opts & PV_DUPLICATE) || !included[idx])
-        {
-          (*v)[(*nv)++] = add;
-          if (!(pv_opts & PV_DUPLICATE))
-            included[idx] = 1;
-        }
+      (*v)[(*nv)++] = add;
+      if (included != NULL)
+        included[idx] = 1;
     }
 }
 
     }
 }
 
@@ -243,16 +239,16 @@ parse_var_set_vars (const struct var_set *vs,
   else
     included = NULL;
 
   else
     included = NULL;
 
-  if (lex_match (T_ALL))
-    add_variables (v, nv, &mv, included, pv_opts,
-                   vs, 0, var_set_get_cnt (vs) - 1, DC_ORDINARY);
-  else 
+  do
     {
     {
-      do
+      if (lex_match (T_ALL))
+        add_variables (v, nv, &mv, included, pv_opts,
+                       vs, 0, var_set_get_cnt (vs) - 1, DC_ORDINARY);
+      else 
         {
           enum dict_class class;
           size_t first_idx;
         {
           enum dict_class class;
           size_t first_idx;
-          
+
           if (!parse_var_idx_class (vs, &first_idx, &class))
             goto fail;
 
           if (!parse_var_idx_class (vs, &first_idx, &class))
             goto fail;
 
@@ -293,13 +289,15 @@ parse_var_set_vars (const struct var_set *vs,
 
               add_variables (v, nv, &mv, included, pv_opts,
                              vs, first_idx, last_idx, class);
 
               add_variables (v, nv, &mv, included, pv_opts,
                              vs, first_idx, last_idx, class);
-            }
-          if (pv_opts & PV_SINGLE)
-            break;
-          lex_match (',');
+            } 
         }
         }
-      while (token == T_ID && var_set_lookup_var (vs, tokid) != NULL);
+
+      if (pv_opts & PV_SINGLE)
+        break;
+      lex_match (',');
     }
     }
+  while (token == T_ALL
+         || (token == T_ID && var_set_lookup_var (vs, tokid) != NULL));
   
   if (*nv == 0)
     goto fail;
   
   if (*nv == 0)
     goto fail;
index 369c08a3496ddece04b6554d0eb71660eee4d71f..6a7ec65d22c341e44294071c8187f0dc3ccd67b7 100644 (file)
@@ -1,3 +1,12 @@
+Tue Jul  4 09:59:52 2006  Ben Pfaff  <blp@gnu.org>
+
+       Fix bug #15766 (/KEEP subcommand on SAVE doesn't fully support
+       ALL) and additional underlying system file issues.
+       
+       * automake.mk: Add keep-all.sh to TESTS.
+       
+       * bugs/keep-all.sh: New test.
+
 Mon Jul  3 21:09:52 2006  Ben Pfaff  <blp@gnu.org>
 
        Modify the random distributions test to verify to 2 more decimal
 Mon Jul  3 21:09:52 2006  Ben Pfaff  <blp@gnu.org>
 
        Modify the random distributions test to verify to 2 more decimal
index 7843b5eb9e7c9dcf6146d3f260fb6162cc80d5e7..5cd421cc4b021ea192af4481316a772bd20a612b 100644 (file)
@@ -92,6 +92,7 @@ TESTS = \
        tests/bugs/compute-lv.sh \
        tests/bugs/temp-freq.sh \
        tests/bugs/print-crash.sh \
        tests/bugs/compute-lv.sh \
        tests/bugs/temp-freq.sh \
        tests/bugs/print-crash.sh \
+       tests/bugs/keep-all.sh \
        tests/xforms/casefile.sh \
        tests/stats/descript-basic.sh \
        tests/stats/descript-missing.sh \
        tests/xforms/casefile.sh \
        tests/stats/descript-basic.sh \
        tests/stats/descript-missing.sh \
diff --git a/tests/bugs/keep-all.sh b/tests/bugs/keep-all.sh
new file mode 100755 (executable)
index 0000000..bd3deca
--- /dev/null
@@ -0,0 +1,89 @@
+#!/bin/sh
+
+# This program tests for bug #15766 (/KEEP subcommand on SAVE doesn't
+# fully support ALL) and underlying problems.
+
+TEMPDIR=/tmp/pspp-tst-$$
+TESTFILE=$TEMPDIR/`basename $0`.pspp
+
+# ensure that top_builddir  are absolute
+if [ -z "$top_builddir" ] ; then top_builddir=. ; fi
+if [ -z "$top_srcdir" ] ; then top_srcdir=. ; fi
+top_builddir=`cd $top_builddir; pwd`
+PSPP=$top_builddir/src/ui/terminal/pspp
+
+# ensure that top_srcdir is absolute
+top_srcdir=`cd $top_srcdir; pwd`
+
+STAT_CONFIG_PATH=$top_srcdir/config
+export STAT_CONFIG_PATH
+
+
+cleanup()
+{
+     cd /
+     rm -rf $TEMPDIR
+}
+
+
+fail()
+{
+    echo $activity
+    echo FAILED
+    cleanup;
+    exit 1;
+}
+
+
+no_result()
+{
+    echo $activity
+    echo NO RESULT;
+    cleanup;
+    exit 2;
+}
+
+pass()
+{
+    cleanup;
+    exit 0;
+}
+
+mkdir -p $TEMPDIR
+
+cd $TEMPDIR
+
+for mode in COMPRESSED UNCOMPRESSED; do
+    activity="create program ($mode)"
+    cat > $TESTFILE <<EOF
+DATA LIST LIST NOTABLE 
+       /a b c d e f g h i j k l m n o p q r s t u v w x y z (F2.0).
+BEGIN DATA.
+1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
+END DATA.
+LIST.
+SAVE OUTFILE='test.sav'/$mode.
+GET FILE='test.sav'/KEEP=x y z all.
+LIST.
+EOF
+    if [ $? -ne 0 ] ; then no_result ; fi
+
+    activity="run PSPP ($mode)"
+    $SUPERVISOR $PSPP -o raw-ascii $TESTFILE
+    if [ $? -ne 0 ] ; then no_result ; fi
+
+
+    activity="compare output ($mode)"
+    perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
+    diff -u -b  -w $TEMPDIR/pspp.list - << EOF
+ a  b  c  d  e  f  g  h  i  j  k  l  m  n  o  p  q  r  s  t  u  v  w  x  y  z
+-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
+ 1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 
+ x  y  z  a  b  c  d  e  f  g  h  i  j  k  l  m  n  o  p  q  r  s  t  u  v  w
+-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
+24 25 26  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 
+EOF
+    if [ $? -ne 0 ] ; then fail ; fi
+done
+
+pass;