Adopt use of gnulib for portability.
[pspp] / src / data-list.c
index c9203122134f87eac200e04b6fc984520c5486ec..2b088860bf71f7493995bb160852db1556828ef4 100644 (file)
@@ -14,8 +14,8 @@
 
    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
-   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
-   02111-1307, USA. */
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301, USA. */
 
 #include <config.h>
 #include "data-list.h"
 #include <stdio.h>
 #include <stdlib.h>
 #include "alloc.h"
+#include "case.h"
 #include "command.h"
 #include "data-in.h"
 #include "debug-print.h"
-#include "dfm.h"
+#include "dfm-read.h"
+#include "dictionary.h"
 #include "error.h"
 #include "file-handle.h"
 #include "format.h"
@@ -39,6 +41,9 @@
 #include "tab.h"
 #include "var.h"
 #include "vfm.h"
+
+#include "gettext.h"
+#define _(msgid) gettext (msgid)
 \f
 /* Utility function. */
 
@@ -63,7 +68,7 @@ struct dls_var_spec
     int fc, lc;                        /* Column numbers in record. */
 
     /* Free format only. */
-    char name[9];              /* Name of variable. */
+    char name[LONG_NAME_LEN + 1]; /* Name of variable. */
   };
 
 /* Constants for DATA LIST type. */
@@ -81,12 +86,12 @@ struct data_list_pgm
     struct trns_header h;
 
     struct dls_var_spec *first, *last; /* Variable parsing specifications. */
-    struct file_handle *handle;        /* Input file, never NULL. */
+    struct dfm_reader *reader;  /* Data file reader. */
 
     int type;                  /* A DLS_* constant. */
     struct variable *end;      /* Variable specified on END subcommand. */
     int eof;                   /* End of file encountered. */
-    int nrec;                  /* Number of records. */
+    int rec_cnt;                /* Number of records. */
     size_t case_size;           /* Case size in bytes. */
     char *delims;               /* Delimiters if any; not null-terminated. */
     size_t delim_cnt;           /* Number of delimiter, or 0 for spaces. */
@@ -94,9 +99,10 @@ struct data_list_pgm
 
 static int parse_fixed (struct data_list_pgm *);
 static int parse_free (struct dls_var_spec **, struct dls_var_spec **);
-static void dump_fixed_table (const struct dls_var_spec *specs,
-                              const struct file_handle *handle, int nrec);
-static void dump_free_table (const struct data_list_pgm *);
+static void dump_fixed_table (const struct dls_var_spec *,
+                              const struct file_handle *, int rec_cnt);
+static void dump_free_table (const struct data_list_pgm *,
+                             const struct file_handle *);
 static void destroy_dls_var_spec (struct dls_var_spec *);
 static trns_free_func data_list_trns_free;
 static trns_proc_func data_list_trns_proc;
@@ -107,21 +113,19 @@ static trns_proc_func data_list_trns_proc;
 int
 cmd_data_list (void)
 {
-  /* DATA LIST program under construction. */
-  struct data_list_pgm *dls;
-
-  /* 0=print no table, 1=print table.  (TABLE subcommand.)  */
-  int table = -1;
+  struct data_list_pgm *dls;     /* DATA LIST program under construction. */
+  int table = -1;                /* Print table if nonzero, -1=undecided. */
+  struct file_handle *fh = NULL; /* File handle of source, NULL=inline file. */
 
   if (!case_source_is_complex (vfm_source))
     discard_variables ();
 
   dls = xmalloc (sizeof *dls);
-  dls->handle = default_handle;
+  dls->reader = NULL;
   dls->type = -1;
   dls->end = NULL;
   dls->eof = 0;
-  dls->nrec = 0;
+  dls->rec_cnt = 0;
   dls->delims = NULL;
   dls->delim_cnt = 0;
   dls->first = dls->last = NULL;
@@ -131,11 +135,11 @@ cmd_data_list (void)
       if (lex_match_id ("FILE"))
        {
          lex_match ('=');
-         dls->handle = fh_parse_file_handle ();
-         if (!dls->handle)
+         fh = fh_parse ();
+         if (fh == NULL)
            goto error;
          if (case_source_is_class (vfm_source, &file_type_source_class)
-              && dls->handle != default_handle)
+              && fh != default_handle)
            {
              msg (SE, _("DATA LIST may not use a different file from "
                         "that specified on its surrounding FILE TYPE."));
@@ -148,7 +152,7 @@ cmd_data_list (void)
          lex_match ('(');
          if (!lex_force_int ())
            goto error;
-         dls->nrec = lex_integer ();
+         dls->rec_cnt = lex_integer ();
          lex_get ();
          lex_match (')');
        }
@@ -207,7 +211,10 @@ cmd_data_list (void)
                       if (lex_match_id ("TAB"))
                         delim = '\t';
                       else if (token == T_STRING && tokstr.length == 1)
-                        delim = tokstr.string[0];
+                       {
+                         delim = tokstr.string[0];
+                         lex_get();
+                       }
                       else 
                         {
                           lex_error (NULL);
@@ -230,7 +237,7 @@ cmd_data_list (void)
     }
 
   dls->case_size = dict_get_case_size (default_dict);
-  default_handle = dls->handle;
+  default_handle = fh;
 
   if (dls->type == -1)
     dls->type = DLS_FIXED;
@@ -248,38 +255,34 @@ cmd_data_list (void)
       if (!parse_fixed (dls))
        goto error;
       if (table)
-       dump_fixed_table (dls->first, dls->handle, dls->nrec);
+       dump_fixed_table (dls->first, fh, dls->rec_cnt);
     }
   else
     {
       if (!parse_free (&dls->first, &dls->last))
        goto error;
       if (table)
-       dump_free_table (dls);
+       dump_free_table (dls, fh);
     }
 
-  if (!dfm_open_for_reading (dls->handle))
+  dls->reader = dfm_open_reader (fh);
+  if (dls->reader == NULL)
     goto error;
 
   if (vfm_source != NULL)
     {
-      struct data_list_pgm *new_pgm;
-
       dls->h.proc = data_list_trns_proc;
       dls->h.free = data_list_trns_free;
-
-      new_pgm = xmalloc (sizeof *new_pgm);
-      memcpy (new_pgm, &dls, sizeof *new_pgm);
-      add_transformation (&new_pgm->h);
+      add_transformation (&dls->h);
     }
   else 
-    vfm_source = create_case_source (&data_list_source_class,
-                                     default_dict, dls);
+    vfm_source = create_case_source (&data_list_source_class, dls);
 
   return CMD_SUCCESS;
 
  error:
   destroy_dls_var_spec (dls->first);
+  free (dls->delims);
   free (dls);
   return CMD_FAILURE;
 }
@@ -344,7 +347,7 @@ parse_fixed (struct data_list_pgm *dls)
       while (lex_match ('/'))
        {
          fx.recno++;
-         if (lex_integer_p ())
+         if (lex_is_integer ())
            {
              if (lex_integer () < fx.recno)
                {
@@ -365,7 +368,7 @@ parse_fixed (struct data_list_pgm *dls)
       if (!parse_DATA_LIST_vars (&fx.name, &fx.name_cnt, PV_NONE))
        return 0;
 
-      if (token == T_NUM)
+      if (lex_is_number ())
        {
          if (!fixed_parse_compatible (&fx, &dls->first, &dls->last))
            goto fail;
@@ -378,7 +381,7 @@ parse_fixed (struct data_list_pgm *dls)
       else
        {
          msg (SE, _("SPSS-like or FORTRAN-like format "
-              "specification expected after variable names."));
+                     "specification expected after variable names."));
          goto fail;
        }
 
@@ -391,20 +394,15 @@ parse_fixed (struct data_list_pgm *dls)
       msg (SE, _("At least one variable must be specified."));
       return 0;
     }
-  if (dls->nrec && dls->last->rec > dls->nrec)
+  if (dls->rec_cnt && dls->last->rec > dls->rec_cnt)
     {
       msg (SE, _("Variables are specified on records that "
                 "should not exist according to RECORDS subcommand."));
       return 0;
     }
-  else if (!dls->nrec)
-    dls->nrec = dls->last->rec;
-  if (token != '.')
-    {
-      lex_error (_("expecting end of command"));
-      return 0;
-    }
-  return 1;
+  else if (!dls->rec_cnt)
+    dls->rec_cnt = dls->last->rec;
+  return lex_end_of_command () == CMD_SUCCESS;
 
 fail:
   for (i = 0; i < fx.name_cnt; i++)
@@ -495,7 +493,7 @@ fixed_parse_compatible (struct fixed_parsing_state *fx,
       else
        input.type = FMT_F;
 
-      if (lex_integer_p ())
+      if (lex_is_integer ())
        {
          if (lex_integer () < 1)
            {
@@ -529,7 +527,7 @@ fixed_parse_compatible (struct fixed_parsing_state *fx,
       input.type = FMT_F;
       input.d = 0;
     }
-  if (!check_input_specifier (&input))
+  if (!check_input_specifier (&input, 1))
     return 0;
 
   /* Start column for next specification. */
@@ -707,7 +705,7 @@ fixed_parse_fortran_internal (struct fixed_parsing_state *fx,
       tail = new;
 
       /* Parse count. */
-      if (lex_integer_p ())
+      if (lex_is_integer ())
        {
          new->count = lex_integer ();
          lex_get ();
@@ -725,8 +723,8 @@ fixed_parse_fortran_internal (struct fixed_parsing_state *fx,
        }
       else if (lex_match ('/'))
        new->f.type = FMT_NEWREC;
-      else if (!parse_format_specifier (&new->f, 1)
-              || !check_input_specifier (&new->f))
+      else if (!parse_format_specifier (&new->f, FMTP_ALLOW_XT)
+              || !check_input_specifier (&new->f, 1))
        goto fail;
 
       lex_match (',');
@@ -776,12 +774,10 @@ fixed_parse_fortran (struct fixed_parsing_state *fx,
    ending column. */
 static void
 dump_fixed_table (const struct dls_var_spec *specs,
-                  const struct file_handle *handle, int nrec)
+                  const struct file_handle *fh, int rec_cnt)
 {
   const struct dls_var_spec *spec;
   struct tab_table *t;
-  char *buf;
-  const char *filename;
   int i;
 
   for (i = 0, spec = specs; spec; spec = spec->next)
@@ -807,21 +803,16 @@ dump_fixed_table (const struct dls_var_spec *specs,
                    fmt_to_string (&spec->input));
     }
 
-  filename = handle_get_filename (handle);
-  if (filename == NULL)
-    filename = "";
-  buf = local_alloc (strlen (filename) + INT_DIGITS + 80);
-  sprintf (buf, (handle != inline_file
-                 ? ngettext ("Reading %d record from file %s.",
-                             "Reading %d records from file %s.", nrec)
-                 : ngettext ("Reading %d record from the command file.",
-                             "Reading %d records from the command file.",
-                             nrec)),
-           nrec, filename);
-  
-  tab_title (t, 0, buf);
+  if (fh != NULL)
+    tab_title (t, 1, ngettext ("Reading %d record from file %s.",
+                               "Reading %d records from file %s.", rec_cnt),
+               rec_cnt, handle_get_filename (fh));
+  else
+    tab_title (t, 1, ngettext ("Reading %d record from the command file.",
+                               "Reading %d records from the command file.",
+                               rec_cnt),
+               rec_cnt);
   tab_submit (t);
-  local_free (buf);
 }
 \f
 /* Free-format parsing. */
@@ -843,10 +834,11 @@ parse_free (struct dls_var_spec **first, struct dls_var_spec **last)
 
       if (!parse_DATA_LIST_vars (&name, &name_cnt, PV_NONE))
        return 0;
+
       if (lex_match ('('))
        {
          if (!parse_format_specifier (&input, 0)
-              || !check_input_specifier (&input)
+              || !check_input_specifier (&input, 1)
               || !lex_force_match (')')) 
             {
               for (i = 0; i < name_cnt; i++)
@@ -859,10 +851,8 @@ parse_free (struct dls_var_spec **first, struct dls_var_spec **last)
       else
        {
          lex_match ('*');
-         input.type = FMT_F;
-         input.w = 8;
-         input.d = 0;
-         output = get_format();
+          input = make_input_format (FMT_F, 8, 0);
+         output = get_format ();
        }
 
       if (input.type == FMT_A || input.type == FMT_AHEX)
@@ -875,6 +865,7 @@ parse_free (struct dls_var_spec **first, struct dls_var_spec **last)
          struct variable *v;
 
          v = dict_create_var (default_dict, name[i], width);
+         
          if (!v)
            {
              msg (SE, _("%s is a duplicate variable name."), name[i]);
@@ -889,7 +880,7 @@ parse_free (struct dls_var_spec **first, struct dls_var_spec **last)
           spec->input = input;
           spec->v = v;
          spec->fv = v->fv;
-         strcpy (spec->name, name[i]);
+         str_copy_trunc (spec->name, sizeof spec->name, v->name);
          append_var_spec (first, last, spec);
        }
       for (i = 0; i < name_cnt; i++)
@@ -897,15 +888,14 @@ parse_free (struct dls_var_spec **first, struct dls_var_spec **last)
       free (name);
     }
 
-  if (token != '.')
-    lex_error (_("expecting end of command"));
-  return 1;
+  return lex_end_of_command () == CMD_SUCCESS;
 }
 
 /* Displays a table giving information on free-format variable parsing
    on DATA LIST. */
 static void
-dump_free_table (const struct data_list_pgm *dls)
+dump_free_table (const struct data_list_pgm *dls,
+                 const struct file_handle *fh)
 {
   struct tab_table *t;
   int i;
@@ -934,19 +924,12 @@ dump_free_table (const struct data_list_pgm *dls)
        tab_text (t, 1, i, TAB_LEFT | TAT_FIX, fmt_to_string (&spec->input));
       }
   }
-  
-  {
-    const char *filename;
-
-    filename = handle_get_filename (dls->handle);
-    if (filename == NULL)
-      filename = "";
-    tab_title (t, 1,
-              (dls->handle != inline_file
-               ? _("Reading free-form data from file %s.")
-               : _("Reading free-form data from the command file.")),
-              filename);
-  }
+
+  if (fh != NULL)
+    tab_title (t, 1, _("Reading free-form data from file %s."),
+               handle_get_filename (fh));
+  else
+    tab_title (t, 1, _("Reading free-form data from the command file."));
   
   tab_submit (t);
 }
@@ -963,18 +946,18 @@ dump_free_table (const struct data_list_pgm *dls)
    a 1-based column number indicating the beginning of the field
    on success. */
 static int
-cut_field (const struct data_list_pgm *dls, struct len_string *field,
+cut_field (const struct data_list_pgm *dls, struct fixed_string *field,
            int *end_blank)
 {
-  struct len_string line;
+  struct fixed_string line;
   char *cp;
   size_t column_start;
 
-  if (dfm_eof (dls->handle))
+  if (dfm_eof (dls->reader))
     return 0;
   if (dls->delim_cnt == 0)
-    dfm_expand_tabs (dls->handle);
-  dfm_get_record (dls->handle, &line);
+    dfm_expand_tabs (dls->reader);
+  dfm_get_record (dls->reader, &line);
 
   cp = ls_c_str (&line);
   if (dls->delim_cnt == 0) 
@@ -1018,7 +1001,7 @@ cut_field (const struct data_list_pgm *dls, struct len_string *field,
     {
       if (cp >= ls_end (&line)) 
         {
-          int column = dfm_column_start (dls->handle);
+          int column = dfm_column_start (dls->reader);
                /* A blank line or a line that ends in \t has a
              trailing blank field. */
           if (column == 1 || (column > 1 && cp[-1] == '\t'))
@@ -1028,7 +1011,7 @@ cut_field (const struct data_list_pgm *dls, struct len_string *field,
                   *end_blank = 1;
                   field->string = ls_end (&line);
                   field->length = 0;
-                  dfm_forward_record (dls->handle);
+                  dfm_forward_record (dls->reader);
                   return column;
                 }
               else 
@@ -1052,10 +1035,10 @@ cut_field (const struct data_list_pgm *dls, struct len_string *field,
         }
     }
   
-  dfm_forward_columns (dls->handle, field->string - line.string);
-  column_start = dfm_column_start (dls->handle);
+  dfm_forward_columns (dls->reader, field->string - line.string);
+  column_start = dfm_column_start (dls->reader);
     
-  dfm_forward_columns (dls->handle, cp - field->string);
+  dfm_forward_columns (dls->reader, cp - field->string);
     
   return column_start;
 }
@@ -1097,21 +1080,21 @@ read_from_data_list_fixed (const struct data_list_pgm *dls,
   struct dls_var_spec *var_spec = dls->first;
   int i;
 
-  if (dfm_eof (dls->handle))
+  if (dfm_eof (dls->reader))
     return -2;
-  for (i = 1; i <= dls->nrec; i++)
+  for (i = 1; i <= dls->rec_cnt; i++)
     {
-      struct len_string line;
+      struct fixed_string line;
       
-      if (dfm_eof (dls->handle))
+      if (dfm_eof (dls->reader))
        {
          /* Note that this can't occur on the first record. */
          msg (SW, _("Partial case of %d of %d records discarded."),
-              i - 1, dls->nrec);
+              i - 1, dls->rec_cnt);
          return -2;
        }
-      dfm_expand_tabs (dls->handle);
-      dfm_get_record (dls->handle, &line);
+      dfm_expand_tabs (dls->reader);
+      dfm_get_record (dls->reader, &line);
 
       for (; var_spec && i == var_spec->rec; var_spec = var_spec->next)
        {
@@ -1119,15 +1102,15 @@ read_from_data_list_fixed (const struct data_list_pgm *dls,
 
          data_in_finite_line (&di, ls_c_str (&line), ls_length (&line),
                                var_spec->fc, var_spec->lc);
-         di.v = &c->data[var_spec->fv];
-         di.flags = 0;
+         di.v = case_data_rw (c, var_spec->fv);
+         di.flags = DI_IMPLIED_DECIMALS;
          di.f1 = var_spec->fc;
          di.format = var_spec->input;
 
          data_in (&di);
        }
 
-      dfm_forward_record (dls->handle);
+      dfm_forward_record (dls->reader);
     }
 
   return -1;
@@ -1145,7 +1128,7 @@ read_from_data_list_free (const struct data_list_pgm *dls,
 
   for (var_spec = dls->first; var_spec; var_spec = var_spec->next)
     {
-      struct len_string field;
+      struct fixed_string field;
       int column;
       
       /* Cut out a field and read in a new record if necessary. */
@@ -1155,9 +1138,9 @@ read_from_data_list_free (const struct data_list_pgm *dls,
          if (column != 0)
            break;
 
-         if (!dfm_eof (dls->handle)) 
-            dfm_forward_record (dls->handle);
-         if (dfm_eof (dls->handle))
+         if (!dfm_eof (dls->reader)) 
+            dfm_forward_record (dls->reader);
+         if (dfm_eof (dls->reader))
            {
              if (var_spec != dls->first)
                msg (SW, _("Partial case discarded.  The first variable "
@@ -1171,7 +1154,7 @@ read_from_data_list_free (const struct data_list_pgm *dls,
 
        di.s = ls_c_str (&field);
        di.e = ls_end (&field);
-       di.v = &c->data[var_spec->fv];
+       di.v = case_data_rw (c, var_spec->fv);
        di.flags = 0;
        di.f1 = column;
        di.format = var_spec->input;
@@ -1191,12 +1174,12 @@ read_from_data_list_list (const struct data_list_pgm *dls,
   struct dls_var_spec *var_spec;
   int end_blank = 0;
 
-  if (dfm_eof (dls->handle))
+  if (dfm_eof (dls->reader))
     return -2;
 
   for (var_spec = dls->first; var_spec; var_spec = var_spec->next)
     {
-      struct len_string field;
+      struct fixed_string field;
       int column;
 
       /* Cut out a field and check for end-of-line. */
@@ -1212,9 +1195,9 @@ read_from_data_list_list (const struct data_list_pgm *dls,
             {
               int width = get_format_var_width (&var_spec->input);
               if (width == 0)
-                c->data[var_spec->fv].f = SYSMIS;
+                case_data_rw (c, var_spec->fv)->f = SYSMIS;
               else
-                memset (c->data[var_spec->fv].s, ' ', width); 
+                memset (case_data_rw (c, var_spec->fv)->s, ' ', width); 
             }
          break;
        }
@@ -1224,7 +1207,7 @@ read_from_data_list_list (const struct data_list_pgm *dls,
 
        di.s = ls_c_str (&field);
        di.e = ls_end (&field);
-       di.v = &c->data[var_spec->fv];
+       di.v = case_data_rw (c, var_spec->fv);
        di.flags = 0;
        di.f1 = column;
        di.format = var_spec->input;
@@ -1232,7 +1215,7 @@ read_from_data_list_list (const struct data_list_pgm *dls,
       }
     }
 
-  dfm_forward_record (dls->handle);
+  dfm_forward_record (dls->reader);
   return -1;
 }
 
@@ -1255,9 +1238,9 @@ static void
 data_list_trns_free (struct trns_header *pgm)
 {
   struct data_list_pgm *dls = (struct data_list_pgm *) pgm;
+  free (dls->delims);
   destroy_dls_var_spec (dls->first);
-  fh_close_handle (dls->handle);
-  free (pgm);
+  dfm_close_reader (dls->reader);
 }
 
 /* Handle DATA LIST transformation T, parsing data into C. */
@@ -1269,7 +1252,7 @@ data_list_trns_proc (struct trns_header *t, struct ccase *c,
   data_list_read_func *read_func;
   int retval;
 
-  dfm_push (dls->handle);
+  dfm_push (dls->reader);
 
   read_func = get_data_list_read_func (dls);
   retval = read_func (dls, c);
@@ -1283,7 +1266,7 @@ data_list_trns_proc (struct trns_header *t, struct ccase *c,
         {
           msg (SE, _("Attempt to read past end of file."));
           err_failure ();
-          dfm_pop (dls->handle);
+          dfm_pop (dls->reader);
           return -2;
         }
 
@@ -1298,14 +1281,14 @@ data_list_trns_proc (struct trns_header *t, struct ccase *c,
     {
       if (retval == -2)
         {
-          c->data[dls->end->fv].f = 1.0;
+          case_data_rw (c, dls->end->fv)->f = 1.0;
           retval = -1;
         }
       else
-        c->data[dls->end->fv].f = 0.0;
+        case_data_rw (c, dls->end->fv)->f = 0.0;
     }
   
-  dfm_pop (dls->handle);
+  dfm_pop (dls->reader);
 
   return retval;
 }
@@ -1320,13 +1303,11 @@ data_list_source_read (struct case_source *source,
   struct data_list_pgm *dls = source->aux;
   data_list_read_func *read_func = get_data_list_read_func (dls);
 
-  dfm_push (dls->handle);
+  dfm_push (dls->reader);
   while (read_func (dls, c) != -2)
     if (!write_case (wc_data))
       break;
-  dfm_pop (dls->handle);
-
-  fh_close_handle (dls->handle);
+  dfm_pop (dls->reader);
 }
 
 /* Destroys the source's internal data. */
@@ -1334,6 +1315,7 @@ static void
 data_list_source_destroy (struct case_source *source)
 {
   data_list_trns_free (source->aux);
+  free (source->aux);
 }
 
 const struct case_source_class data_list_source_class = 
@@ -1358,7 +1340,7 @@ struct repeating_data_trns
   {
     struct trns_header h;
     struct dls_var_spec *first, *last; /* Variable parsing specifications. */
-    struct file_handle *handle;        /* Input file, never NULL. */
+    struct dfm_reader *reader;         /* Input file, never NULL. */
 
     struct rpd_num_or_var starts_beg;  /* STARTS=, before the dash. */
     struct rpd_num_or_var starts_end;  /* STARTS=, after the dash. */
@@ -1389,17 +1371,18 @@ int
 cmd_repeating_data (void)
 {
   struct repeating_data_trns *rpd;
-
-  /* 0=print no table, 1=print table.  (TABLE subcommand.)  */
-  int table = 1;
-
-  /* Bits are set when a particular subcommand has been seen. */
-  unsigned seen = 0;
+  int table = 1;                /* Print table? */
+  bool saw_starts = false;      /* Saw STARTS subcommand? */
+  bool saw_occurs = false;      /* Saw OCCURS subcommand? */
+  bool saw_length = false;      /* Saw LENGTH subcommand? */
+  bool saw_continued = false;   /* Saw CONTINUED subcommand? */
+  bool saw_id = false;          /* Saw ID subcommand? */
+  struct file_handle *const fh = default_handle;
   
   assert (case_source_is_complex (vfm_source));
 
   rpd = xmalloc (sizeof *rpd);
-  rpd->handle = default_handle;
+  rpd->reader = dfm_open_reader (default_handle);
   rpd->first = rpd->last = NULL;
   rpd->starts_beg.num = 0;
   rpd->starts_beg.var = NULL;
@@ -1415,11 +1398,12 @@ cmd_repeating_data (void)
     {
       if (lex_match_id ("FILE"))
        {
+          struct file_handle *file;
          lex_match ('=');
-         rpd->handle = fh_parse_file_handle ();
-         if (!rpd->handle)
+         file = fh_parse ();
+         if (file == NULL)
            goto error;
-         if (rpd->handle != default_handle)
+         if (file != fh)
            {
              msg (SE, _("REPEATING DATA must use the same file as its "
                         "corresponding DATA LIST or FILE TYPE."));
@@ -1429,13 +1413,13 @@ cmd_repeating_data (void)
       else if (lex_match_id ("STARTS"))
        {
          lex_match ('=');
-         if (seen & 1)
+         if (saw_starts)
            {
              msg (SE, _("%s subcommand given multiple times."),"STARTS");
              goto error;
            }
-         seen |= 1;
-
+          saw_starts = true;
+          
          if (!parse_num_or_var (&rpd->starts_beg, "STARTS beginning column"))
            goto error;
 
@@ -1445,11 +1429,10 @@ cmd_repeating_data (void)
              if (!parse_num_or_var (&rpd->starts_end, "STARTS ending column"))
                goto error;
            } else {
-             /* Otherwise, rpd->starts_end is left uninitialized.
-                This is okay.  We will initialize it later from the
-                record length of the file.  We can't do this now
-                because we can't be sure that the user has specified
-                the file handle yet. */
+             /* Otherwise, rpd->starts_end is uninitialized.  We
+                will initialize it later from the record length
+                of the file.  We can't do so now because the
+                file handle may not be specified yet. */
            }
 
          if (rpd->starts_beg.num != 0 && rpd->starts_end.num != 0
@@ -1464,12 +1447,12 @@ cmd_repeating_data (void)
       else if (lex_match_id ("OCCURS"))
        {
          lex_match ('=');
-         if (seen & 2)
+         if (saw_occurs)
            {
              msg (SE, _("%s subcommand given multiple times."),"OCCURS");
              goto error;
            }
-         seen |= 2;
+         saw_occurs |= 2;
 
          if (!parse_num_or_var (&rpd->occurs, "OCCURS"))
            goto error;
@@ -1477,12 +1460,12 @@ cmd_repeating_data (void)
       else if (lex_match_id ("LENGTH"))
        {
          lex_match ('=');
-         if (seen & 4)
+         if (saw_length & 4)
            {
              msg (SE, _("%s subcommand given multiple times."),"LENGTH");
              goto error;
            }
-         seen |= 4;
+         saw_length |= 4;
 
          if (!parse_num_or_var (&rpd->length, "LENGTH"))
            goto error;
@@ -1490,16 +1473,17 @@ cmd_repeating_data (void)
       else if (lex_match_id ("CONTINUED"))
        {
          lex_match ('=');
-         if (seen & 8)
+         if (saw_continued & 8)
            {
              msg (SE, _("%s subcommand given multiple times."),"CONTINUED");
              goto error;
            }
-         seen |= 8;
+         saw_continued |= 8;
 
          if (!lex_match ('/'))
            {
-             if (!parse_num_or_var (&rpd->cont_beg, "CONTINUED beginning column"))
+             if (!parse_num_or_var (&rpd->cont_beg,
+                                     "CONTINUED beginning column"))
                goto error;
 
              lex_negative_to_dash ();
@@ -1523,12 +1507,12 @@ cmd_repeating_data (void)
       else if (lex_match_id ("ID"))
        {
          lex_match ('=');
-         if (seen & 16)
+         if (saw_id & 16)
            {
              msg (SE, _("%s subcommand given multiple times."),"ID");
              goto error;
            }
-         seen |= 16;
+         saw_id |= 16;
          
          if (!lex_force_int ())
            goto error;
@@ -1592,58 +1576,71 @@ cmd_repeating_data (void)
     }
 
   /* Comes here when DATA specification encountered. */
-  if ((seen & (1 | 2)) != (1 | 2))
+  if (!saw_starts || !saw_occurs)
     {
-      if ((seen & 1) == 0)
+      if (!saw_starts)
        msg (SE, _("Missing required specification STARTS."));
-      if ((seen & 2) == 0)
+      if (!saw_occurs)
        msg (SE, _("Missing required specification OCCURS."));
       goto error;
     }
 
   /* Enforce ID restriction. */
-  if ((seen & 16) && !(seen & 8))
+  if (saw_id && !saw_continued)
     {
       msg (SE, _("ID specified without CONTINUED."));
       goto error;
     }
 
-  /* Calculate starts_end, cont_end if necessary. */
-  if (rpd->starts_end.num == 0 && rpd->starts_end.var == NULL)
-    rpd->starts_end.num = handle_get_record_width (rpd->handle);
-  if (rpd->cont_end.num == 0 && rpd->starts_end.var == NULL)
-    rpd->cont_end.num = handle_get_record_width (rpd->handle);
-      
-  /* Calculate length if possible. */
-  if ((seen & 4) == 0)
+  /* Calculate and check starts_end, cont_end if necessary. */
+  if (rpd->starts_end.num == 0 && rpd->starts_end.var == NULL) 
     {
-      struct dls_var_spec *iter;
-      
-      for (iter = rpd->first; iter; iter = iter->next)
-       {
-         if (iter->lc > rpd->length.num)
-           rpd->length.num = iter->lc;
-       }
-      assert (rpd->length.num != 0);
+      rpd->starts_end.num = fh != NULL ? handle_get_record_width (fh) : 80;
+      if (rpd->starts_beg.num != 0 
+          && rpd->starts_beg.num > rpd->starts_end.num)
+        {
+          msg (SE, _("STARTS beginning column (%d) exceeds "
+                     "default STARTS ending column taken from file's "
+                     "record width (%d)."),
+               rpd->starts_beg.num, rpd->starts_end.num);
+          goto error;
+        } 
+    }
+  if (rpd->cont_end.num == 0 && rpd->cont_end.var == NULL) 
+    {
+      rpd->cont_end.num = fh != NULL ? handle_get_record_width (fh) : 80;
+      if (rpd->cont_beg.num != 0
+          && rpd->cont_beg.num > rpd->cont_end.num)
+        {
+          msg (SE, _("CONTINUED beginning column (%d) exceeds "
+                     "default CONTINUED ending column taken from file's "
+                     "record width (%d)."),
+               rpd->cont_beg.num, rpd->cont_end.num);
+          goto error;
+        } 
     }
   
   lex_match ('=');
   if (!parse_repeating_data (&rpd->first, &rpd->last))
     goto error;
 
+  /* Calculate length if necessary. */
+  if (!saw_length)
+    {
+      struct dls_var_spec *iter;
+      
+      for (iter = rpd->first; iter; iter = iter->next)
+        if (iter->lc > rpd->length.num)
+          rpd->length.num = iter->lc;
+      assert (rpd->length.num != 0);
+    }
+  
   if (table)
-    dump_fixed_table (rpd->first, rpd->handle, rpd->last->rec);
+    dump_fixed_table (rpd->first, fh, rpd->last->rec);
 
-  {
-    struct repeating_data_trns *new_trns;
-
-    rpd->h.proc = repeating_data_trns_proc;
-    rpd->h.free = repeating_data_trns_free;
-
-    new_trns = xmalloc (sizeof *new_trns);
-    memcpy (new_trns, &rpd, sizeof *new_trns);
-    add_transformation ((struct trns_header *) new_trns);
-  }
+  rpd->h.proc = repeating_data_trns_proc;
+  rpd->h.free = repeating_data_trns_free;
+  add_transformation (&rpd->h);
 
   return lex_end_of_command ();
 
@@ -1700,7 +1697,7 @@ parse_num_or_var (struct rpd_num_or_var *value, const char *message)
          return 0;
        }
     }
-  else if (lex_integer_p ())
+  else if (lex_is_integer ())
     {
       value->num = lex_integer ();
       
@@ -1735,7 +1732,7 @@ parse_repeating_data (struct dls_var_spec **first, struct dls_var_spec **last)
       if (!parse_DATA_LIST_vars (&fx.name, &fx.name_cnt, PV_NONE))
        return 0;
 
-      if (token == T_NUM)
+      if (lex_is_number ())
        {
          if (!fixed_parse_compatible (&fx, first, last))
            goto fail;
@@ -1748,7 +1745,7 @@ parse_repeating_data (struct dls_var_spec **first, struct dls_var_spec **last)
       else
        {
          msg (SE, _("SPSS-like or FORTRAN-like format "
-              "specification expected after variable names."));
+                     "specification expected after variable names."));
          goto fail;
        }
 
@@ -1756,11 +1753,6 @@ parse_repeating_data (struct dls_var_spec **first, struct dls_var_spec **last)
        free (fx.name[i]);
       free (fx.name);
     }
-  if (token != '.')
-    {
-      lex_error (_("expecting end of command"));
-      return 0;
-    }
   
   return 1;
 
@@ -1778,21 +1770,13 @@ parse_repeating_data (struct dls_var_spec **first, struct dls_var_spec **last)
 static int
 realize_value (struct rpd_num_or_var *n, struct ccase *c)
 {
-  if (n->num > 0)
-    return n->num;
-  
-  assert (n->num == 0);
   if (n->var != NULL)
     {
-      double v = c->data[n->var->fv].f;
-
-      if (v == SYSMIS || v <= INT_MIN || v >= INT_MAX)
-       return -1;
-      else
-       return v;
+      double v = case_num (c, n->var->fv);
+      return v != SYSMIS && v >= INT_MIN && v <= INT_MAX ? v : -1;
     }
   else
-    return 0;
+    return n->num;
 }
 
 /* Parameter record passed to rpd_parse_record(). */
@@ -1891,7 +1875,7 @@ rpd_parse_record (const struct rpd_parse_info *info)
                struct data_in di;
 
                data_in_finite_line (&di, info->line, info->len, fc, lc);
-               di.v = &info->c->data[var_spec->fv];
+               di.v = case_data_rw (info->c, var_spec->fv);
                di.flags = 0;
                di.f1 = fc + 1;
                di.format = var_spec->input;
@@ -1921,7 +1905,7 @@ repeating_data_trns_proc (struct trns_header *trns, struct ccase *c,
 {
   struct repeating_data_trns *t = (struct repeating_data_trns *) trns;
     
-  struct len_string line;       /* Current record. */
+  struct fixed_string line;       /* Current record. */
 
   int starts_beg;      /* Starting column. */
   int starts_end;      /* Ending column. */
@@ -1936,15 +1920,15 @@ repeating_data_trns_proc (struct trns_header *trns, struct ccase *c,
     
   int skip_first_record = 0;
     
-  dfm_push (t->handle);
+  dfm_push (t->reader);
   
   /* Read the current record. */
-  dfm_reread_record (t->handle, 1);
-  dfm_expand_tabs (t->handle);
-  if (dfm_eof (t->handle))
+  dfm_reread_record (t->reader, 1);
+  dfm_expand_tabs (t->reader);
+  if (dfm_eof (t->reader))
     return -2;
-  dfm_get_record (t->handle, &line);
-  dfm_forward_record (t->handle);
+  dfm_get_record (t->reader, &line);
+  dfm_forward_record (t->reader);
 
   /* Calculate occurs, length. */
   occurs_left = occurs = realize_value (&t->occurs, c);
@@ -2034,7 +2018,7 @@ repeating_data_trns_proc (struct trns_header *trns, struct ccase *c,
       assert (occurs_left >= 0);
 
       /* Read in another record. */
-      if (dfm_eof (t->handle))
+      if (dfm_eof (t->reader))
         {
           tmsg (SE, RPD_ERR,
                 _("Unexpected end of file with %d repetitions "
@@ -2042,9 +2026,9 @@ repeating_data_trns_proc (struct trns_header *trns, struct ccase *c,
                 occurs_left, occurs);
           return -2;
         }
-      dfm_expand_tabs (t->handle);
-      dfm_get_record (t->handle, &line);
-      dfm_forward_record (t->handle);
+      dfm_expand_tabs (t->reader);
+      dfm_get_record (t->reader, &line);
+      dfm_forward_record (t->reader);
 
       /* Parse this record. */
       info.trns = t;
@@ -2062,7 +2046,7 @@ repeating_data_trns_proc (struct trns_header *trns, struct ccase *c,
       occurs_left -= code;
     }
     
-  dfm_pop (t->handle);
+  dfm_pop (t->reader);
 
   /* FIXME: This is a kluge until we've implemented multiplexing of
      transformations. */
@@ -2076,7 +2060,7 @@ repeating_data_trns_free (struct trns_header *rpd_)
   struct repeating_data_trns *rpd = (struct repeating_data_trns *) rpd_;
 
   destroy_dls_var_spec (rpd->first);
-  fh_close_handle (rpd->handle);
+  dfm_close_reader (rpd->reader);
   free (rpd->id_value);
 }