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. */
-/* AIX requires this to be the first thing in the file. */
#include <config.h>
-#if __GNUC__
-#define alloca __builtin_alloca
-#else
-#if HAVE_ALLOCA_H
-#include <alloca.h>
-#else
-#ifdef _AIX
-#pragma alloca
-#else
-#ifndef alloca /* predefined by HP cc +Olibcalls */
-char *alloca ();
-#endif
-#endif
-#endif
-#endif
-
-#include <assert.h>
+#include "data-list.h"
+#include "error.h"
#include <ctype.h>
#include <float.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"
#include "tab.h"
#include "var.h"
#include "vfm.h"
+
+#include "gettext.h"
+#define _(msgid) gettext (msgid)
\f
/* Utility function. */
/* Describes how to parse one variable. */
struct dls_var_spec
{
- struct dls_var_spec *next;
+ struct dls_var_spec *next; /* Next specification in list. */
+
+ /* Both free and fixed formats. */
+ struct fmt_spec input; /* Input format of this field. */
struct variable *v; /* Associated variable. Used only in
parsing. Not safe later. */
- char name[9]; /* Free-format: Name of variable. */
- int rec; /* Fixed-format: Record number (1-based). */
- int fc, lc; /* Fixed-format: Column numbers in record. */
- struct fmt_spec input; /* Input format of this field. */
int fv; /* First value in case. */
- int width; /* 0=numeric, >0=width of alpha field. */
+
+ /* Fixed format only. */
+ int rec; /* Record number (1-based). */
+ int fc, lc; /* Column numbers in record. */
+
+ /* Free format only. */
+ char name[LONG_NAME_LEN + 1]; /* Name of variable. */
};
/* Constants for DATA LIST type. */
/* DATA LIST private data structure. */
struct data_list_pgm
{
- struct trns_header h;
- struct dls_var_spec *spec; /* Variable parsing specifications. */
- struct file_handle *handle; /* Input file, never NULL. */
- /* Do not reorder preceding fields. */
+ struct dls_var_spec *first, *last; /* Variable parsing specifications. */
+ 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. */
};
-/* Holds information on parsing the data file. */
-static struct data_list_pgm dls;
-
-/* Pointer to a pointer to where the first dls_var_spec should go. */
-static struct dls_var_spec **first;
-
-/* Last dls_var_spec in the chain. Used for building the linked-list. */
-static struct dls_var_spec *next;
-
-static int parse_fixed (void);
-static int parse_free (void);
-static void dump_fixed_table (void);
-static void dump_free_table (void);
-static void destroy_dls (struct trns_header *);
-static int read_one_case (struct trns_header *, struct ccase *);
+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 *,
+ 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;
/* Message title for REPEATING DATA. */
#define RPD_ERR "REPEATING DATA: "
int
cmd_data_list (void)
{
- /* 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. */
- lex_match_id ("DATA");
- lex_match_id ("LIST");
-
- if (vfm_source != &input_program_source
- && vfm_source != &file_type_source)
+ if (!case_source_is_complex (vfm_source))
discard_variables ();
- dls.handle = default_handle;
- dls.type = -1;
- dls.end = NULL;
- dls.eof = 0;
- dls.nrec = 0;
- dls.spec = NULL;
- next = NULL;
- first = &dls.spec;
+ dls = xmalloc (sizeof *dls);
+ dls->reader = NULL;
+ dls->type = -1;
+ dls->end = NULL;
+ dls->eof = 0;
+ dls->rec_cnt = 0;
+ dls->delims = NULL;
+ dls->delim_cnt = 0;
+ dls->first = dls->last = NULL;
while (token != '/')
{
if (lex_match_id ("FILE"))
{
lex_match ('=');
- dls.handle = fh_parse_file_handle ();
- if (!dls.handle)
- return CMD_FAILURE;
- if (vfm_source == &file_type_source && dls.handle != default_handle)
+ fh = fh_parse ();
+ if (fh == NULL)
+ goto error;
+ if (case_source_is_class (vfm_source, &file_type_source_class)
+ && fh != default_handle)
{
msg (SE, _("DATA LIST may not use a different file from "
"that specified on its surrounding FILE TYPE."));
- return CMD_FAILURE;
+ goto error;
}
}
else if (lex_match_id ("RECORDS"))
lex_match ('=');
lex_match ('(');
if (!lex_force_int ())
- return CMD_FAILURE;
- dls.nrec = lex_integer ();
+ goto error;
+ dls->rec_cnt = lex_integer ();
lex_get ();
lex_match (')');
}
else if (lex_match_id ("END"))
{
- if (dls.end)
+ if (dls->end)
{
msg (SE, _("The END subcommand may only be specified once."));
- return CMD_FAILURE;
+ goto error;
}
lex_match ('=');
if (!lex_force_id ())
- return CMD_FAILURE;
- dls.end = dict_lookup_var (default_dict, tokid);
- if (!dls.end)
- {
- dls.end = dict_create_var (default_dict, tokid, 0);
- assert (dls.end != NULL);
- }
+ goto error;
+ dls->end = dict_lookup_var (default_dict, tokid);
+ if (!dls->end)
+ dls->end = dict_create_var_assert (default_dict, tokid, 0);
lex_get ();
}
else if (token == T_ID)
{
- /* Must match DLS_* constants. */
- static const char *id[] = {"FIXED", "FREE", "LIST", "NOTABLE",
- "TABLE", NULL};
- const char **p;
- int index;
-
- for (p = id; *p; p++)
- if (lex_id_match (*p, tokid))
- break;
- if (*p == NULL)
- {
- lex_error (NULL);
- return CMD_FAILURE;
- }
-
- lex_get ();
-
- index = p - id;
- if (index < 3)
- {
- if (dls.type != -1)
+ if (lex_match_id ("NOTABLE"))
+ table = 0;
+ else if (lex_match_id ("TABLE"))
+ table = 1;
+ else
+ {
+ int type;
+ if (lex_match_id ("FIXED"))
+ type = DLS_FIXED;
+ else if (lex_match_id ("FREE"))
+ type = DLS_FREE;
+ else if (lex_match_id ("LIST"))
+ type = DLS_LIST;
+ else
+ {
+ lex_error (NULL);
+ goto error;
+ }
+
+ if (dls->type != -1)
{
msg (SE, _("Only one of FIXED, FREE, or LIST may "
- "be specified."));
- return CMD_FAILURE;
+ "be specified."));
+ goto error;
}
-
- dls.type = index;
- }
- else
- table = index - 3;
- }
+ dls->type = type;
+
+ if ((dls->type == DLS_FREE || dls->type == DLS_LIST)
+ && lex_match ('('))
+ {
+ while (!lex_match (')'))
+ {
+ int delim;
+
+ if (lex_match_id ("TAB"))
+ delim = '\t';
+ else if (token == T_STRING && tokstr.length == 1)
+ {
+ delim = tokstr.string[0];
+ lex_get();
+ }
+ else
+ {
+ lex_error (NULL);
+ goto error;
+ }
+
+ dls->delims = xrealloc (dls->delims, dls->delim_cnt + 1);
+ dls->delims[dls->delim_cnt++] = delim;
+
+ lex_match (',');
+ }
+ }
+ }
+ }
else
{
lex_error (NULL);
- return CMD_FAILURE;
+ goto error;
}
}
- default_handle = dls.handle;
+ dls->case_size = dict_get_case_size (default_dict);
+ default_handle = fh;
- if (dls.type == -1)
- dls.type = DLS_FIXED;
+ if (dls->type == -1)
+ dls->type = DLS_FIXED;
if (table == -1)
{
- if (dls.type == DLS_FREE)
+ if (dls->type == DLS_FREE)
table = 0;
else
table = 1;
}
- if (dls.type == DLS_FIXED)
+ if (dls->type == DLS_FIXED)
{
- if (!parse_fixed ())
- return CMD_FAILURE;
+ if (!parse_fixed (dls))
+ goto error;
if (table)
- dump_fixed_table ();
+ dump_fixed_table (dls->first, fh, dls->rec_cnt);
}
else
{
- if (!parse_free ())
- return CMD_FAILURE;
+ if (!parse_free (&dls->first, &dls->last))
+ goto error;
if (table)
- dump_free_table ();
+ dump_free_table (dls, fh);
}
- if (vfm_source != NULL)
- {
- struct data_list_pgm *new_pgm;
+ dls->reader = dfm_open_reader (fh);
+ if (dls->reader == NULL)
+ goto error;
- dls.h.proc = read_one_case;
- dls.h.free = destroy_dls;
-
- new_pgm = xmalloc (sizeof *new_pgm);
- memcpy (new_pgm, &dls, sizeof *new_pgm);
- add_transformation ((struct trns_header *) new_pgm);
- }
- else
- vfm_source = &data_list_source;
+ if (vfm_source != NULL)
+ add_transformation (data_list_trns_proc, data_list_trns_free, dls);
+ else
+ vfm_source = create_case_source (&data_list_source_class, dls);
return CMD_SUCCESS;
+
+ error:
+ data_list_trns_free (dls);
+ return CMD_FAILURE;
}
+/* Adds SPEC to the linked list with head at FIRST and tail at
+ LAST. */
static void
-append_var_spec (struct dls_var_spec *spec)
+append_var_spec (struct dls_var_spec **first, struct dls_var_spec **last,
+ struct dls_var_spec *spec)
{
- if (next == 0)
- *first = next = xmalloc (sizeof *spec);
- else
- next = next->next = xmalloc (sizeof *spec);
+ spec->next = NULL;
- memcpy (next, spec, sizeof *spec);
- next->next = NULL;
+ if (*first == NULL)
+ *first = spec;
+ else
+ (*last)->next = spec;
+ *last = spec;
}
\f
/* Fixed-format parsing. */
struct fmt_list *down;
};
-/* Used as "local" variables among the fixed-format parsing funcs. If
- it were guaranteed that PSPP were going to be compiled by gcc,
- I'd make all these functions a single set of nested functions. */
-static struct
+/* State of parsing DATA LIST. */
+struct fixed_parsing_state
{
char **name; /* Variable names. */
- int nname; /* Number of names. */
- int cname; /* dump_fmt_list: index of next name to use. */
+ size_t name_cnt; /* Number of names. */
int recno; /* Index of current record. */
int sc; /* 1-based column number of starting column for
next field to output. */
+ };
- struct dls_var_spec spec; /* Next specification to output. */
- int fc, lc; /* First, last column in set of fields specified
- together. */
-
- int level; /* Nesting level in fixed_parse_fortran(). */
- }
-fx;
-
-static int fixed_parse_compatible (void);
-static struct fmt_list *fixed_parse_fortran (void);
+static int fixed_parse_compatible (struct fixed_parsing_state *,
+ struct dls_var_spec **,
+ struct dls_var_spec **);
+static int fixed_parse_fortran (struct fixed_parsing_state *,
+ struct dls_var_spec **,
+ struct dls_var_spec **);
+/* Parses all the variable specifications for DATA LIST FIXED,
+ storing them into DLS. Returns nonzero if successful. */
static int
-parse_fixed (void)
+parse_fixed (struct data_list_pgm *dls)
{
- int i;
+ struct fixed_parsing_state fx;
+ size_t i;
fx.recno = 0;
fx.sc = 1;
while (lex_match ('/'))
{
fx.recno++;
- if (lex_integer_p ())
+ if (lex_is_integer ())
{
if (lex_integer () < fx.recno)
{
}
fx.sc = 1;
}
- fx.spec.rec = fx.recno;
- if (!parse_DATA_LIST_vars (&fx.name, &fx.nname, PV_NONE))
+ 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 ())
+ if (!fixed_parse_compatible (&fx, &dls->first, &dls->last))
goto fail;
}
else if (token == '(')
{
- fx.level = 0;
- fx.cname = 0;
- if (!fixed_parse_fortran ())
+ if (!fixed_parse_fortran (&fx, &dls->first, &dls->last))
goto fail;
}
else
{
msg (SE, _("SPSS-like or FORTRAN-like format "
- "specification expected after variable names."));
+ "specification expected after variable names."));
goto fail;
}
- for (i = 0; i < fx.nname; i++)
+ for (i = 0; i < fx.name_cnt; i++)
free (fx.name[i]);
free (fx.name);
}
- if (dls.nrec && next->rec > dls.nrec)
+ if (dls->first == NULL)
{
- msg (SE, _("Variables are specified on records that "
- "should not exist according to RECORDS subcommand."));
+ msg (SE, _("At least one variable must be specified."));
return 0;
}
- else if (!dls.nrec)
- dls.nrec = next->rec;
- if (token != '.')
+ if (dls->rec_cnt && dls->last->rec > dls->rec_cnt)
{
- lex_error (_("expecting end of command"));
+ msg (SE, _("Variables are specified on records that "
+ "should not exist according to RECORDS subcommand."));
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.nname; i++)
+ for (i = 0; i < fx.name_cnt; i++)
free (fx.name[i]);
free (fx.name);
return 0;
}
+/* Parses a variable specification in the form 1-10 (A) based on
+ FX and adds specifications to the linked list with head at
+ FIRST and tail at LAST. */
static int
-fixed_parse_compatible (void)
+fixed_parse_compatible (struct fixed_parsing_state *fx,
+ struct dls_var_spec **first, struct dls_var_spec **last)
{
- int dividend;
+ struct fmt_spec input;
+ int fc, lc;
+ int width;
int i;
+ /* First column. */
if (!lex_force_int ())
return 0;
-
- fx.fc = lex_integer ();
- if (fx.fc < 1)
+ fc = lex_integer ();
+ if (fc < 1)
{
msg (SE, _("Column positions for fields must be positive."));
return 0;
}
lex_get ();
+ /* Last column. */
lex_negative_to_dash ();
if (lex_match ('-'))
{
if (!lex_force_int ())
return 0;
- fx.lc = lex_integer ();
- if (fx.lc < 1)
+ lc = lex_integer ();
+ if (lc < 1)
{
msg (SE, _("Column positions for fields must be positive."));
return 0;
}
- else if (fx.lc < fx.fc)
+ else if (lc < fc)
{
msg (SE, _("The ending column for a field must be "
"greater than the starting column."));
lex_get ();
}
else
- fx.lc = fx.fc;
+ lc = fc;
+
+ /* Divide columns evenly. */
+ input.w = (lc - fc + 1) / fx->name_cnt;
+ if ((lc - fc + 1) % fx->name_cnt)
+ {
+ msg (SE, _("The %d columns %d-%d "
+ "can't be evenly divided into %d fields."),
+ lc - fc + 1, fc, lc, fx->name_cnt);
+ return 0;
+ }
- fx.spec.input.w = fx.lc - fx.fc + 1;
+ /* Format specifier. */
if (lex_match ('('))
{
struct fmt_desc *fdp;
{
const char *cp;
- fx.spec.input.type = parse_format_specifier_name (&cp, 0);
- if (fx.spec.input.type == -1)
+ input.type = parse_format_specifier_name (&cp, 0);
+ if (input.type == -1)
return 0;
if (*cp)
{
lex_match (',');
}
else
- fx.spec.input.type = FMT_F;
+ input.type = FMT_F;
- if (lex_integer_p ())
+ if (lex_is_integer ())
{
if (lex_integer () < 1)
{
return 0;
}
- fx.spec.input.d = lex_integer ();
+ input.d = lex_integer ();
lex_get ();
}
else
- fx.spec.input.d = 0;
+ input.d = 0;
- fdp = &formats[fx.spec.input.type];
- if (fdp->n_args < 2 && fx.spec.input.d)
+ fdp = &formats[input.type];
+ if (fdp->n_args < 2 && input.d)
{
msg (SE, _("Input format %s doesn't accept decimal places."),
fdp->name);
return 0;
}
- if (fx.spec.input.d > 16)
- fx.spec.input.d = 16;
+ if (input.d > 16)
+ input.d = 16;
if (!lex_force_match (')'))
return 0;
}
else
{
- fx.spec.input.type = FMT_F;
- fx.spec.input.d = 0;
+ input.type = FMT_F;
+ input.d = 0;
}
+ if (!check_input_specifier (&input, 1))
+ return 0;
- fx.sc = fx.lc + 1;
-
- if ((fx.lc - fx.fc + 1) % fx.nname)
- {
- msg (SE, _("The %d columns %d-%d "
- "can't be evenly divided into %d fields."),
- fx.lc - fx.fc + 1, fx.fc, fx.lc, fx.nname);
- return 0;
- }
+ /* Start column for next specification. */
+ fx->sc = lc + 1;
- dividend = (fx.lc - fx.fc + 1) / fx.nname;
- fx.spec.input.w = dividend;
- if (!check_input_specifier (&fx.spec.input))
- return 0;
+ /* Width of variables to create. */
+ if (input.type == FMT_A || input.type == FMT_AHEX)
+ width = input.w;
+ else
+ width = 0;
- for (i = 0; i < fx.nname; i++)
+ /* Create variables and var specs. */
+ for (i = 0; i < fx->name_cnt; i++)
{
- int type;
- int width;
+ struct dls_var_spec *spec;
struct variable *v;
- if (fx.spec.input.type == FMT_A || fx.spec.input.type == FMT_AHEX)
- {
- type = ALPHA;
- width = dividend;
- }
- else
- {
- type = NUMERIC;
- width = 0;
- }
-
- v = dict_create_var (default_dict, fx.name[i], width);
- if (v)
+ v = dict_create_var (default_dict, fx->name[i], width);
+ if (v != NULL)
{
- convert_fmt_ItoO (&fx.spec.input, &v->print);
+ convert_fmt_ItoO (&input, &v->print);
v->write = v->print;
+ if (!case_source_is_complex (vfm_source))
+ v->init = 0;
}
else
{
- v = dict_lookup_var (default_dict, fx.name[i]);
- assert (v != NULL);
- if (!vfm_source)
+ v = dict_lookup_var_assert (default_dict, fx->name[i]);
+ if (vfm_source == NULL)
{
- msg (SE, _("%s is a duplicate variable name."), fx.name[i]);
+ msg (SE, _("%s is a duplicate variable name."), fx->name[i]);
return 0;
}
- if (type != v->type)
+ if ((width != 0) != (v->width != 0))
{
msg (SE, _("There is already a variable %s of a "
"different type."),
- fx.name[i]);
+ fx->name[i]);
return 0;
}
- if (type == ALPHA && dividend != v->width)
+ if (width != 0 && width != v->width)
{
msg (SE, _("There is already a string variable %s of a "
- "different width."), fx.name[i]);
+ "different width."), fx->name[i]);
return 0;
}
}
- fx.spec.v = v;
- fx.spec.fc = fx.fc + dividend * i;
- fx.spec.lc = fx.spec.fc + dividend - 1;
- fx.spec.fv = v->fv;
- fx.spec.width = v->width;
- append_var_spec (&fx.spec);
+ spec = xmalloc (sizeof *spec);
+ spec->input = input;
+ spec->v = v;
+ spec->fv = v->fv;
+ spec->rec = fx->recno;
+ spec->fc = fc + input.w * i;
+ spec->lc = spec->fc + input.w - 1;
+ append_var_spec (first, last, spec);
}
return 1;
}
-/* Destroy a format list and, optionally, all its sublists. */
+/* Destroy format list F and, if RECURSE is nonzero, all its
+ sublists. */
static void
destroy_fmt_list (struct fmt_list *f, int recurse)
{
}
/* Takes a hierarchically structured fmt_list F as constructed by
- fixed_parse_fortran(), and flattens it into a linear list of
- dls_var_spec's. */
+ fixed_parse_fortran(), and flattens it, adding the variable
+ specifications to the linked list with head FIRST and tail
+ LAST. NAME_IDX is used to take values from the list of names
+ in FX; it should initially point to a value of 0. */
static int
-dump_fmt_list (struct fmt_list *f)
+dump_fmt_list (struct fixed_parsing_state *fx, struct fmt_list *f,
+ struct dls_var_spec **first, struct dls_var_spec **last,
+ int *name_idx)
{
int i;
for (; f; f = f->next)
if (f->f.type == FMT_X)
- fx.sc += f->count;
+ fx->sc += f->count;
else if (f->f.type == FMT_T)
- fx.sc = f->f.w;
+ fx->sc = f->f.w;
else if (f->f.type == FMT_NEWREC)
{
- fx.recno += f->count;
- fx.sc = 1;
+ fx->recno += f->count;
+ fx->sc = 1;
}
else
for (i = 0; i < f->count; i++)
if (f->f.type == FMT_DESCEND)
{
- if (!dump_fmt_list (f->down))
+ if (!dump_fmt_list (fx, f->down, first, last, name_idx))
return 0;
}
else
{
- int type;
+ struct dls_var_spec *spec;
int width;
struct variable *v;
if (formats[f->f.type].cat & FCAT_STRING)
- {
- type = ALPHA;
- width = f->f.w;
- }
- else
- {
- type = NUMERIC;
- width = 0;
- }
- if (fx.cname >= fx.nname)
+ width = f->f.w;
+ else
+ width = 0;
+ if (*name_idx >= fx->name_cnt)
{
msg (SE, _("The number of format "
- "specifications exceeds the number of "
- "variable names given."));
+ "specifications exceeds the given number of "
+ "variable names."));
return 0;
}
- fx.spec.v = v = dict_create_var (default_dict,
- fx.name[fx.cname++],
- width);
+ v = dict_create_var (default_dict, fx->name[(*name_idx)++], width);
if (!v)
{
- msg (SE, _("%s is a duplicate variable name."), fx.name[i]);
+ msg (SE, _("%s is a duplicate variable name."), fx->name[i]);
return 0;
}
- fx.spec.input = f->f;
- convert_fmt_ItoO (&fx.spec.input, &v->print);
+ if (!case_source_is_complex (vfm_source))
+ v->init = 0;
+
+ spec = xmalloc (sizeof *spec);
+ spec->v = v;
+ spec->input = f->f;
+ spec->fv = v->fv;
+ spec->rec = fx->recno;
+ spec->fc = fx->sc;
+ spec->lc = fx->sc + f->f.w - 1;
+ append_var_spec (first, last, spec);
+
+ convert_fmt_ItoO (&spec->input, &v->print);
v->write = v->print;
- fx.spec.rec = fx.recno;
- fx.spec.fc = fx.sc;
- fx.spec.lc = fx.sc + f->f.w - 1;
- fx.spec.fv = v->fv;
- fx.spec.width = v->width;
- append_var_spec (&fx.spec);
-
- fx.sc += f->f.w;
+ fx->sc += f->f.w;
}
return 1;
}
-/* Calls itself recursively to parse nested levels of parentheses.
- Returns to its original caller: NULL, to indicate error; non-NULL,
- but nothing useful, to indicate success (it returns a free()'d
- block). */
+/* Recursively parses a FORTRAN-like format specification into
+ the linked list with head FIRST and tail TAIL. LEVEL is the
+ level of recursion, starting from 0. Returns the parsed
+ specification if successful, or a null pointer on failure. */
static struct fmt_list *
-fixed_parse_fortran (void)
+fixed_parse_fortran_internal (struct fixed_parsing_state *fx,
+ struct dls_var_spec **first,
+ struct dls_var_spec **last)
{
- struct fmt_list *head;
- struct fmt_list *fl = NULL;
+ struct fmt_list *head = NULL;
+ struct fmt_list *tail = NULL;
- lex_get (); /* Skip opening parenthesis. */
+ lex_force_match ('(');
while (token != ')')
{
- if (fl)
- fl = fl->next = xmalloc (sizeof *fl);
+ /* New fmt_list. */
+ struct fmt_list *new = xmalloc (sizeof *new);
+ new->next = NULL;
+
+ /* Append new to list. */
+ if (head != NULL)
+ tail->next = new;
else
- head = fl = xmalloc (sizeof *fl);
+ head = new;
+ tail = new;
- if (lex_integer_p ())
+ /* Parse count. */
+ if (lex_is_integer ())
{
- fl->count = lex_integer ();
+ new->count = lex_integer ();
lex_get ();
}
else
- fl->count = 1;
+ new->count = 1;
+ /* Parse format specifier. */
if (token == '(')
{
- fl->f.type = FMT_DESCEND;
- fx.level++;
- fl->down = fixed_parse_fortran ();
- fx.level--;
- if (!fl->down)
+ new->f.type = FMT_DESCEND;
+ new->down = fixed_parse_fortran_internal (fx, first, last);
+ if (new->down == NULL)
goto fail;
}
else if (lex_match ('/'))
- fl->f.type = FMT_NEWREC;
- else if (!parse_format_specifier (&fl->f, 1)
- || !check_input_specifier (&fl->f))
+ new->f.type = FMT_NEWREC;
+ else if (!parse_format_specifier (&new->f, FMTP_ALLOW_XT)
+ || !check_input_specifier (&new->f, 1))
goto fail;
lex_match (',');
}
- fl->next = NULL;
- lex_get ();
-
- if (fx.level)
- return head;
+ lex_force_match (')');
- fl->next = NULL;
- dump_fmt_list (head);
- if (fx.cname < fx.nname)
- {
- msg (SE, _("There aren't enough format specifications "
- "to match the number of variable names given."));
- goto fail;
- }
- destroy_fmt_list (head, 1);
return head;
fail:
- fl->next = NULL;
destroy_fmt_list (head, 0);
return NULL;
}
+/* Parses a FORTRAN-like format specification into the linked
+ list with head FIRST and tail LAST. Returns nonzero if
+ successful. */
+static int
+fixed_parse_fortran (struct fixed_parsing_state *fx,
+ struct dls_var_spec **first, struct dls_var_spec **last)
+{
+ struct fmt_list *list;
+ int name_idx;
+
+ list = fixed_parse_fortran_internal (fx, first, last);
+ if (list == NULL)
+ return 0;
+
+ name_idx = 0;
+ dump_fmt_list (fx, list, first, last, &name_idx);
+ destroy_fmt_list (list, 1);
+ if (name_idx < fx->name_cnt)
+ {
+ msg (SE, _("There aren't enough format specifications "
+ "to match the number of variable names given."));
+ return 0;
+ }
+
+ return 1;
+}
+
/* Displays a table giving information on fixed-format variable
parsing on DATA LIST. */
/* FIXME: The `Columns' column should be divided into three columns,
column; then right-justify the starting column and left-justify the
ending column. */
static void
-dump_fixed_table (void)
+dump_fixed_table (const struct dls_var_spec *specs,
+ const struct file_handle *fh, int rec_cnt)
{
- struct dls_var_spec *spec;
+ const struct dls_var_spec *spec;
struct tab_table *t;
- char *buf;
- const char *filename;
int i;
- for (i = 0, spec = *first; spec; spec = spec->next)
+ for (i = 0, spec = specs; spec; spec = spec->next)
i++;
t = tab_create (4, i + 1, 0);
tab_columns (t, TAB_COL_DOWN, 1);
tab_hline (t, TAL_2, 0, 3, 1);
tab_dim (t, tab_natural_dimensions);
- for (i = 1, spec = *first; spec; spec = spec->next, i++)
+ for (i = 1, spec = specs; spec; spec = spec->next, i++)
{
tab_text (t, 0, i, TAB_LEFT, spec->v->name);
tab_text (t, 1, i, TAT_PRINTF, "%d", spec->rec);
fmt_to_string (&spec->input));
}
- if (*first == dls.spec)
- {
- filename = fh_handle_name (dls.handle);
- if (filename == NULL)
- filename = "";
- buf = local_alloc (strlen (filename) + INT_DIGITS + 80);
- sprintf (buf, (dls.handle != inline_file
- ?
- ngettext("Reading %d record from file %s.",
- "Reading %d records from file %s.",dls.nrec)
- :
- ngettext("Reading %d record from the command file.",
- "Reading %d records from the command file.",
- dls.nrec)),
- dls.nrec, filename);
- }
+ 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
- {
- buf = local_alloc (strlen (_("Occurrence data specifications.")) + 1);
- strcpy (buf, _("Occurrence data specifications."));
- }
-
- tab_title (t, 0, buf);
+ 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);
- fh_handle_name (NULL);
- local_free (buf);
}
\f
/* Free-format parsing. */
+/* Parses variable specifications for DATA LIST FREE and adds
+ them to the linked list with head FIRST and tail LAST.
+ Returns nonzero only if successful. */
static int
-parse_free (void)
+parse_free (struct dls_var_spec **first, struct dls_var_spec **last)
{
- struct dls_var_spec spec;
- struct fmt_spec in, out;
- char **name;
- int nname;
- int i;
-
lex_get ();
while (token != '.')
{
+ struct fmt_spec input, output;
+ char **name;
+ size_t name_cnt;
int width;
+ size_t i;
- if (!parse_DATA_LIST_vars (&name, &nname, PV_NONE))
+ if (!parse_DATA_LIST_vars (&name, &name_cnt, PV_NONE))
return 0;
+
if (lex_match ('('))
{
- if (!parse_format_specifier (&in, 0) || !check_input_specifier (&in))
- goto fail;
- if (!lex_force_match (')'))
- goto fail;
- convert_fmt_ItoO (&in, &out);
+ if (!parse_format_specifier (&input, 0)
+ || !check_input_specifier (&input, 1)
+ || !lex_force_match (')'))
+ {
+ for (i = 0; i < name_cnt; i++)
+ free (name[i]);
+ free (name);
+ return 0;
+ }
+ convert_fmt_ItoO (&input, &output);
}
else
{
lex_match ('*');
- in.type = FMT_F;
- in.w = 8;
- in.d = 0;
- out = set_format;
+ input = make_input_format (FMT_F, 8, 0);
+ output = *get_format ();
}
- spec.input = in;
- if (in.type == FMT_A || in.type == FMT_AHEX)
- width = in.w;
+ if (input.type == FMT_A || input.type == FMT_AHEX)
+ width = input.w;
else
width = 0;
- for (i = 0; i < nname; i++)
+ for (i = 0; i < name_cnt; i++)
{
+ struct dls_var_spec *spec;
struct variable *v;
- spec.v = v = dict_create_var (default_dict, name[i], width);
+ v = dict_create_var (default_dict, name[i], width);
+
if (!v)
{
msg (SE, _("%s is a duplicate variable name."), name[i]);
return 0;
}
-
- v->print = v->write = out;
+ v->print = v->write = output;
- strcpy (spec.name, name[i]);
- spec.fv = v->fv;
- spec.width = width;
- append_var_spec (&spec);
+ if (!case_source_is_complex (vfm_source))
+ v->init = 0;
+
+ spec = xmalloc (sizeof *spec);
+ spec->input = input;
+ spec->v = v;
+ spec->fv = v->fv;
+ str_copy_trunc (spec->name, sizeof spec->name, v->name);
+ append_var_spec (first, last, spec);
}
- for (i = 0; i < nname; i++)
+ for (i = 0; i < name_cnt; i++)
free (name[i]);
free (name);
}
- if (token != '.')
- lex_error (_("expecting end of command"));
- return 1;
-
-fail:
- for (i = 0; i < nname; i++)
- free (name[i]);
- free (name);
- return 0;
+ 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 (void)
+dump_free_table (const struct data_list_pgm *dls,
+ const struct file_handle *fh)
{
struct tab_table *t;
int i;
{
struct dls_var_spec *spec;
- for (i = 0, spec = dls.spec; spec; spec = spec->next)
+ for (i = 0, spec = dls->first; spec; spec = spec->next)
i++;
}
{
struct dls_var_spec *spec;
- for (i = 1, spec = dls.spec; spec; spec = spec->next, i++)
+ for (i = 1, spec = dls->first; spec; spec = spec->next, i++)
{
tab_text (t, 0, i, TAB_LEFT, spec->v->name);
tab_text (t, 1, i, TAB_LEFT | TAT_FIX, fmt_to_string (&spec->input));
}
}
-
- {
- const char *filename;
-
- filename = fh_handle_name (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);
- fh_handle_name (NULL);
}
\f
/* Input procedure. */
-/* Pointer to relevant parsing data. Static just to avoid passing it
- around so much. */
-static struct data_list_pgm *dlsp;
-
-/* Extracts a field from the current position in the current record.
- Fields can be unquoted or quoted with single- or double-quote
- characters. *RET_LEN is set to the field length, *RET_CP is set to
- the field itself. After parsing the field, sets the current
- position in the record to just past the field. Returns 0 on
- failure or a 1-based column number indicating the beginning of the
- field on success. */
+/* Extracts a field from the current position in the current
+ record. Fields can be unquoted or quoted with single- or
+ double-quote characters. *FIELD is set to the field content.
+ After parsing the field, sets the current position in the
+ record to just past the field and any trailing delimiter.
+ END_BLANK is used internally; it should be initialized by the
+ caller to 0 and left alone afterward. Returns 0 on failure or
+ a 1-based column number indicating the beginning of the field
+ on success. */
static int
-cut_field (char **ret_cp, int *ret_len)
+cut_field (const struct data_list_pgm *dls, struct fixed_string *field,
+ int *end_blank)
{
- char *cp, *ep;
- int len;
+ struct fixed_string line;
+ char *cp;
+ size_t column_start;
- cp = dfm_get_record (dlsp->handle, &len);
- if (!cp)
+ if (dfm_eof (dls->reader))
return 0;
+ if (dls->delim_cnt == 0)
+ dfm_expand_tabs (dls->reader);
+ dfm_get_record (dls->reader, &line);
- ep = cp + len;
-
- /* Skip leading whitespace and commas. */
- while ((isspace ((unsigned char) *cp) || *cp == ',') && cp < ep)
- cp++;
- if (cp >= ep)
- return 0;
-
- /* Three types of fields: quoted with ', quoted with ", unquoted. */
- if (*cp == '\'' || *cp == '"')
+ cp = ls_c_str (&line);
+ if (dls->delim_cnt == 0)
{
- int quote = *cp;
-
- *ret_cp = ++cp;
- while (cp < ep && *cp != quote)
- cp++;
- *ret_len = cp - *ret_cp;
- if (cp < ep)
- cp++;
+ /* Skip leading whitespace. */
+ while (cp < ls_end (&line) && isspace ((unsigned char) *cp))
+ cp++;
+ if (cp >= ls_end (&line))
+ return 0;
+
+ /* Handle actual data, whether quoted or unquoted. */
+ if (*cp == '\'' || *cp == '"')
+ {
+ int quote = *cp;
+
+ field->string = ++cp;
+ while (cp < ls_end (&line) && *cp != quote)
+ cp++;
+ field->length = cp - field->string;
+ if (cp < ls_end (&line))
+ cp++;
+ else
+ msg (SW, _("Quoted string missing terminating `%c'."), quote);
+ }
else
- msg (SW, _("Scope of string exceeds line."));
+ {
+ field->string = cp;
+ while (cp < ls_end (&line)
+ && !isspace ((unsigned char) *cp) && *cp != ',')
+ cp++;
+ field->length = cp - field->string;
+ }
+
+ /* Skip trailing whitespace and a single comma if present. */
+ while (cp < ls_end (&line) && isspace ((unsigned char) *cp))
+ cp++;
+ if (cp < ls_end (&line) && *cp == ',')
+ cp++;
}
- else
+ else
{
- *ret_cp = cp;
- while (cp < ep && !isspace ((unsigned char) *cp) && *cp != ',')
- cp++;
- *ret_len = cp - *ret_cp;
+ if (cp >= ls_end (&line))
+ {
+ 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'))
+ {
+ if (*end_blank == 0)
+ {
+ *end_blank = 1;
+ field->string = ls_end (&line);
+ field->length = 0;
+ dfm_forward_record (dls->reader);
+ return column;
+ }
+ else
+ {
+ *end_blank = 0;
+ return 0;
+ }
+ }
+ else
+ return 0;
+ }
+ else
+ {
+ field->string = cp;
+ while (cp < ls_end (&line)
+ && memchr (dls->delims, *cp, dls->delim_cnt) == NULL)
+ cp++;
+ field->length = cp - field->string;
+ if (cp < ls_end (&line))
+ cp++;
+ }
}
-
- {
- int beginning_column;
-
- dfm_set_record (dlsp->handle, *ret_cp);
- beginning_column = dfm_get_cur_col (dlsp->handle) + 1;
+
+ dfm_forward_columns (dls->reader, field->string - line.string);
+ column_start = dfm_column_start (dls->reader);
- dfm_set_record (dlsp->handle, cp);
+ dfm_forward_columns (dls->reader, cp - field->string);
- return beginning_column;
- }
+ return column_start;
}
-static int read_from_data_list_fixed (void);
-static int read_from_data_list_free (void);
-static int read_from_data_list_list (void);
-static int do_reading (int flag);
+typedef int data_list_read_func (const struct data_list_pgm *, struct ccase *);
+static data_list_read_func read_from_data_list_fixed;
+static data_list_read_func read_from_data_list_free;
+static data_list_read_func read_from_data_list_list;
-/* FLAG==0: reads any number of cases into temp_case and calls
- write_case() for each one, returns garbage. FLAG!=0: reads one
- case into temp_case and returns -2 on eof, -1 otherwise.
- Uses dlsp as the relevant parsing description. */
-static int
-do_reading (int flag)
+/* Returns the proper function to read the kind of DATA LIST
+ data specified by DLS. */
+static data_list_read_func *
+get_data_list_read_func (const struct data_list_pgm *dls)
{
- int (*func) (void);
-
- int code;
-
- dfm_push (dlsp->handle);
-
- switch (dlsp->type)
+ switch (dls->type)
{
case DLS_FIXED:
- func = read_from_data_list_fixed;
- break;
+ return read_from_data_list_fixed;
+
case DLS_FREE:
- func = read_from_data_list_free;
- break;
+ return read_from_data_list_free;
+
case DLS_LIST:
- func = read_from_data_list_list;
- break;
+ return read_from_data_list_list;
+
default:
assert (0);
+ abort ();
}
- if (flag)
- {
- code = func ();
- if (code == -2)
- {
- if (dlsp->eof == 1)
- {
- msg (SE, _("Attempt to read past end of file."));
- err_failure ();
- return -2;
- }
- dlsp->eof = 1;
- }
- else
- dlsp->eof = 0;
-
- if (dlsp->end != NULL)
- {
- if (code == -2)
- {
- printf ("end of file, setting %s to 1\n", dlsp->end->name);
- temp_case->data[dlsp->end->fv].f = 1.0;
- code = -1;
- }
- else
- {
- printf ("not end of file, setting %s to 0\n", dlsp->end->name);
- temp_case->data[dlsp->end->fv].f = 0.0;
- }
- }
- }
- else
- {
- while (func () != -2)
- if (!write_case ())
- {
- debug_printf ((_("abort in write_case()\n")));
- break;
- }
- fh_close_handle (dlsp->handle);
- }
- dfm_pop (dlsp->handle);
-
- return code;
}
-/* Reads a case from the data file and parses it according to
- fixed-format syntax rules. */
+/* Reads a case from the data file into C, parsing it according
+ to fixed-format syntax rules in DLS. Returns -1 on success,
+ -2 at end of file. */
static int
-read_from_data_list_fixed (void)
+read_from_data_list_fixed (const struct data_list_pgm *dls,
+ struct ccase *c)
{
- struct dls_var_spec *var_spec = dlsp->spec;
+ struct dls_var_spec *var_spec = dls->first;
int i;
- if (!dfm_get_record (dlsp->handle, NULL))
+ if (dfm_eof (dls->reader))
return -2;
- for (i = 1; i <= dlsp->nrec; i++)
+ for (i = 1; i <= dls->rec_cnt; i++)
{
- int len;
- char *line = dfm_get_record (dlsp->handle, &len);
+ struct fixed_string line;
- if (!line)
+ 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, dlsp->nrec);
+ i - 1, dls->rec_cnt);
return -2;
}
+ dfm_expand_tabs (dls->reader);
+ dfm_get_record (dls->reader, &line);
for (; var_spec && i == var_spec->rec; var_spec = var_spec->next)
{
struct data_in di;
- data_in_finite_line (&di, line, len, var_spec->fc, var_spec->lc);
- di.v = &temp_case->data[var_spec->fv];
- di.flags = 0;
+ data_in_finite_line (&di, ls_c_str (&line), ls_length (&line),
+ var_spec->fc, var_spec->lc);
+ 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_fwd_record (dlsp->handle);
+ dfm_forward_record (dls->reader);
}
return -1;
}
-/* Reads a case from the data file and parses it according to
- free-format syntax rules. */
+/* Reads a case from the data file into C, parsing it according
+ to free-format syntax rules in DLS. Returns -1 on success,
+ -2 at end of file. */
static int
-read_from_data_list_free (void)
+read_from_data_list_free (const struct data_list_pgm *dls,
+ struct ccase *c)
{
struct dls_var_spec *var_spec;
- char *field;
- int len;
+ int end_blank = 0;
- for (var_spec = dlsp->spec; var_spec; var_spec = var_spec->next)
+ for (var_spec = dls->first; var_spec; var_spec = var_spec->next)
{
+ struct fixed_string field;
int column;
/* Cut out a field and read in a new record if necessary. */
for (;;)
{
- column = cut_field (&field, &len);
+ column = cut_field (dls, &field, &end_blank);
if (column != 0)
break;
- if (dfm_get_record (dlsp->handle, NULL))
- dfm_fwd_record (dlsp->handle);
- if (!dfm_get_record (dlsp->handle, NULL))
+ if (!dfm_eof (dls->reader))
+ dfm_forward_record (dls->reader);
+ if (dfm_eof (dls->reader))
{
- if (var_spec != dlsp->spec)
+ if (var_spec != dls->first)
msg (SW, _("Partial case discarded. The first variable "
- "missing was %s."), var_spec->name);
+ "missing was %s."), var_spec->name);
return -2;
}
}
{
struct data_in di;
- di.s = field;
- di.e = field + len;
- di.v = &temp_case->data[var_spec->fv];
+ di.s = ls_c_str (&field);
+ di.e = ls_end (&field);
+ di.v = case_data_rw (c, var_spec->fv);
di.flags = 0;
di.f1 = column;
di.format = var_spec->input;
}
/* Reads a case from the data file and parses it according to
- list-format syntax rules. */
+ list-format syntax rules. Returns -1 on success, -2 at end of
+ file. */
static int
-read_from_data_list_list (void)
+read_from_data_list_list (const struct data_list_pgm *dls,
+ struct ccase *c)
{
struct dls_var_spec *var_spec;
- char *field;
- int len;
+ int end_blank = 0;
- if (!dfm_get_record (dlsp->handle, NULL))
+ if (dfm_eof (dls->reader))
return -2;
- for (var_spec = dlsp->spec; var_spec; var_spec = var_spec->next)
+ for (var_spec = dls->first; var_spec; var_spec = var_spec->next)
{
+ struct fixed_string field;
+ int column;
+
/* Cut out a field and check for end-of-line. */
- int column = cut_field (&field, &len);
-
+ column = cut_field (dls, &field, &end_blank);
if (column == 0)
{
- if (set_undefined)
+ if (get_undefined ())
msg (SW, _("Missing value(s) for all variables from %s onward. "
- "These will be filled with the system-missing value "
- "or blanks, as appropriate."),
+ "These will be filled with the system-missing value "
+ "or blanks, as appropriate."),
var_spec->name);
for (; var_spec; var_spec = var_spec->next)
- if (var_spec->width == 0)
- temp_case->data[var_spec->fv].f = SYSMIS;
- else
- memset (temp_case->data[var_spec->fv].s, ' ', var_spec->width);
+ {
+ int width = get_format_var_width (&var_spec->input);
+ if (width == 0)
+ case_data_rw (c, var_spec->fv)->f = SYSMIS;
+ else
+ memset (case_data_rw (c, var_spec->fv)->s, ' ', width);
+ }
break;
}
{
struct data_in di;
- di.s = field;
- di.e = field + len;
- di.v = &temp_case->data[var_spec->fv];
+ di.s = ls_c_str (&field);
+ di.e = ls_end (&field);
+ di.v = case_data_rw (c, var_spec->fv);
di.flags = 0;
di.f1 = column;
di.format = var_spec->input;
}
}
- dfm_fwd_record (dlsp->handle);
+ dfm_forward_record (dls->reader);
return -1;
}
-/* Destroys DATA LIST transformation or input program PGM. */
+/* Destroys SPEC. */
static void
-destroy_dls (struct trns_header *pgm)
+destroy_dls_var_spec (struct dls_var_spec *spec)
{
- struct data_list_pgm *dls = (struct data_list_pgm *) pgm;
- struct dls_var_spec *iter, *next;
+ struct dls_var_spec *next;
- iter = dls->spec;
- while (iter)
+ while (spec != NULL)
{
- next = iter->next;
- free (iter);
- iter = next;
+ next = spec->next;
+ free (spec);
+ spec = next;
}
- fh_close_handle (dls->handle);
}
-/* Note that since this is exclusively an input program, C is
- guaranteed to be temp_case. */
+/* Destroys DATA LIST transformation DLS. */
+static void
+data_list_trns_free (void *dls_)
+{
+ struct data_list_pgm *dls = dls_;
+ free (dls->delims);
+ destroy_dls_var_spec (dls->first);
+ dfm_close_reader (dls->reader);
+ free (dls);
+}
+
+/* Handle DATA LIST transformation DLS, parsing data into C. */
static int
-read_one_case (struct trns_header *t, struct ccase *c unused)
+data_list_trns_proc (void *dls_, struct ccase *c, int case_num UNUSED)
{
- dlsp = (struct data_list_pgm *) t;
- return do_reading (1);
+ struct data_list_pgm *dls = dls_;
+ data_list_read_func *read_func;
+ int retval;
+
+ dfm_push (dls->reader);
+
+ read_func = get_data_list_read_func (dls);
+ retval = read_func (dls, c);
+
+ /* Handle end of file. */
+ if (retval == -2)
+ {
+ /* If we already encountered end of file then this is an
+ error. */
+ if (dls->eof == 1)
+ {
+ msg (SE, _("Attempt to read past end of file."));
+ err_failure ();
+ dfm_pop (dls->reader);
+ return -2;
+ }
+
+ /* Otherwise simply note it. */
+ dls->eof = 1;
+ }
+ else
+ dls->eof = 0;
+
+ /* If there was an END subcommand handle it. */
+ if (dls->end != NULL)
+ {
+ if (retval == -2)
+ {
+ case_data_rw (c, dls->end->fv)->f = 1.0;
+ retval = -1;
+ }
+ else
+ case_data_rw (c, dls->end->fv)->f = 0.0;
+ }
+
+ dfm_pop (dls->reader);
+
+ return retval;
}
\f
/* Reads all the records from the data file and passes them to
write_case(). */
static void
-data_list_source_read (void)
+data_list_source_read (struct case_source *source,
+ struct ccase *c,
+ write_case_func *write_case, write_case_data wc_data)
{
- dlsp = &dls;
- do_reading (0);
+ struct data_list_pgm *dls = source->aux;
+ data_list_read_func *read_func = get_data_list_read_func (dls);
+
+ dfm_push (dls->reader);
+ while (read_func (dls, c) != -2)
+ if (!write_case (wc_data))
+ break;
+ dfm_pop (dls->reader);
}
/* Destroys the source's internal data. */
static void
-data_list_source_destroy_source (void)
+data_list_source_destroy (struct case_source *source)
{
- destroy_dls ((struct trns_header *) & dls);
+ data_list_trns_free (source->aux);
}
-struct case_stream data_list_source =
+const struct case_source_class data_list_source_class =
{
+ "DATA LIST",
NULL,
data_list_source_read,
- NULL,
- NULL,
- data_list_source_destroy_source,
- NULL,
- "DATA LIST",
+ data_list_source_destroy,
};
\f
/* REPEATING DATA. */
/* REPEATING DATA private data structure. */
struct repeating_data_trns
{
- struct trns_header h;
- struct dls_var_spec *spec; /* Variable parsing specifications. */
- struct file_handle *handle; /* Input file, never NULL. */
- /* Do not reorder preceding fields. */
+ struct dls_var_spec *first, *last; /* Variable parsing specifications. */
+ 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. */
struct rpd_num_or_var length; /* LENGTH= subcommand. */
struct rpd_num_or_var cont_beg; /* CONTINUED=, before the dash. */
struct rpd_num_or_var cont_end; /* CONTINUED=, after the dash. */
- int id_beg, id_end; /* ID subcommand, beginning & end columns. */
- struct variable *id_var; /* ID subcommand, DATA LIST variable. */
- struct fmt_spec id_spec; /* ID subcommand, input format spec. */
- };
-/* Information about the transformation being parsed. */
-static struct repeating_data_trns rpd;
+ /* ID subcommand. */
+ int id_beg, id_end; /* Beginning & end columns. */
+ struct variable *id_var; /* DATA LIST variable. */
+ struct fmt_spec id_spec; /* Input format spec. */
+ union value *id_value; /* ID value. */
+
+ write_case_func *write_case;
+ write_case_data wc_data;
+ };
-static int read_one_set_of_repetitions (struct trns_header *, struct ccase *);
+static trns_free_func repeating_data_trns_free;
static int parse_num_or_var (struct rpd_num_or_var *, const char *);
-static int parse_repeating_data (void);
+static int parse_repeating_data (struct dls_var_spec **,
+ struct dls_var_spec **);
static void find_variable_input_spec (struct variable *v,
struct fmt_spec *spec);
int
cmd_repeating_data (void)
{
- /* 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;
-
- lex_match_id ("REPEATING");
- lex_match_id ("DATA");
-
- assert (vfm_source == &input_program_source
- || vfm_source == &file_type_source);
+ struct repeating_data_trns *rpd;
+ 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;
- rpd.handle = default_handle;
- rpd.starts_beg.num = 0;
- rpd.starts_beg.var = NULL;
- rpd.starts_end = rpd.occurs = rpd.length = rpd.cont_beg
- = rpd.cont_end = rpd.starts_beg;
- rpd.id_beg = rpd.id_end = 0;
- rpd.id_var = NULL;
- rpd.spec = NULL;
- first = &rpd.spec;
- next = NULL;
+ assert (case_source_is_complex (vfm_source));
+
+ rpd = xmalloc (sizeof *rpd);
+ rpd->reader = dfm_open_reader (default_handle);
+ rpd->first = rpd->last = NULL;
+ rpd->starts_beg.num = 0;
+ rpd->starts_beg.var = NULL;
+ rpd->starts_end = rpd->occurs = rpd->length = rpd->cont_beg
+ = rpd->cont_end = rpd->starts_beg;
+ rpd->id_beg = rpd->id_end = 0;
+ rpd->id_var = NULL;
+ rpd->id_value = NULL;
lex_match ('/');
{
if (lex_match_id ("FILE"))
{
+ struct file_handle *file;
lex_match ('=');
- rpd.handle = fh_parse_file_handle ();
- if (!rpd.handle)
- return CMD_FAILURE;
- if (rpd.handle != default_handle)
+ file = fh_parse ();
+ if (file == NULL)
+ goto error;
+ if (file != fh)
{
msg (SE, _("REPEATING DATA must use the same file as its "
"corresponding DATA LIST or FILE TYPE."));
- return CMD_FAILURE;
+ goto error;
}
}
else if (lex_match_id ("STARTS"))
{
lex_match ('=');
- if (seen & 1)
+ if (saw_starts)
{
- msg (SE, _("STARTS subcommand given multiple times."));
- return CMD_FAILURE;
+ msg (SE, _("%s subcommand given multiple times."),"STARTS");
+ goto error;
}
- seen |= 1;
-
- if (!parse_num_or_var (&rpd.starts_beg, "STARTS beginning column"))
- return CMD_FAILURE;
+ saw_starts = true;
+
+ if (!parse_num_or_var (&rpd->starts_beg, "STARTS beginning column"))
+ goto error;
lex_negative_to_dash ();
if (lex_match ('-'))
{
- if (!parse_num_or_var (&rpd.starts_end, "STARTS ending column"))
- return CMD_FAILURE;
+ 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
- && rpd.starts_beg.num > rpd.starts_end.num)
+ if (rpd->starts_beg.num != 0 && rpd->starts_end.num != 0
+ && rpd->starts_beg.num > rpd->starts_end.num)
{
msg (SE, _("STARTS beginning column (%d) exceeds "
"STARTS ending column (%d)."),
- rpd.starts_beg.num, rpd.starts_end.num);
- return CMD_FAILURE;
+ rpd->starts_beg.num, rpd->starts_end.num);
+ goto error;
}
}
else if (lex_match_id ("OCCURS"))
{
lex_match ('=');
- if (seen & 2)
+ if (saw_occurs)
{
- msg (SE, _("OCCURS subcommand given multiple times."));
- return CMD_FAILURE;
+ msg (SE, _("%s subcommand given multiple times."),"OCCURS");
+ goto error;
}
- seen |= 2;
+ saw_occurs = true;
- if (!parse_num_or_var (&rpd.occurs, "OCCURS"))
- return CMD_FAILURE;
+ if (!parse_num_or_var (&rpd->occurs, "OCCURS"))
+ goto error;
}
else if (lex_match_id ("LENGTH"))
{
lex_match ('=');
- if (seen & 4)
+ if (saw_length)
{
- msg (SE, _("LENGTH subcommand given multiple times."));
- return CMD_FAILURE;
+ msg (SE, _("%s subcommand given multiple times."),"LENGTH");
+ goto error;
}
- seen |= 4;
+ saw_length = true;
- if (!parse_num_or_var (&rpd.length, "LENGTH"))
- return CMD_FAILURE;
+ if (!parse_num_or_var (&rpd->length, "LENGTH"))
+ goto error;
}
else if (lex_match_id ("CONTINUED"))
{
lex_match ('=');
- if (seen & 8)
+ if (saw_continued)
{
- msg (SE, _("CONTINUED subcommand given multiple times."));
- return CMD_FAILURE;
+ msg (SE, _("%s subcommand given multiple times."),"CONTINUED");
+ goto error;
}
- seen |= 8;
+ saw_continued = true;
if (!lex_match ('/'))
{
- if (!parse_num_or_var (&rpd.cont_beg, "CONTINUED beginning column"))
- return CMD_FAILURE;
+ if (!parse_num_or_var (&rpd->cont_beg,
+ "CONTINUED beginning column"))
+ goto error;
lex_negative_to_dash ();
if (lex_match ('-')
- && !parse_num_or_var (&rpd.cont_end,
+ && !parse_num_or_var (&rpd->cont_end,
"CONTINUED ending column"))
- return CMD_FAILURE;
+ goto error;
- if (rpd.cont_beg.num != 0 && rpd.cont_end.num != 0
- && rpd.cont_beg.num > rpd.cont_end.num)
+ if (rpd->cont_beg.num != 0 && rpd->cont_end.num != 0
+ && rpd->cont_beg.num > rpd->cont_end.num)
{
msg (SE, _("CONTINUED beginning column (%d) exceeds "
"CONTINUED ending column (%d)."),
- rpd.cont_beg.num, rpd.cont_end.num);
- return CMD_FAILURE;
+ rpd->cont_beg.num, rpd->cont_end.num);
+ goto error;
}
}
else
- rpd.cont_beg.num = 1;
+ rpd->cont_beg.num = 1;
}
else if (lex_match_id ("ID"))
{
lex_match ('=');
- if (seen & 16)
+ if (saw_id)
{
- msg (SE, _("ID subcommand given multiple times."));
- return CMD_FAILURE;
+ msg (SE, _("%s subcommand given multiple times."),"ID");
+ goto error;
}
- seen |= 16;
+ saw_id = true;
if (!lex_force_int ())
- return CMD_FAILURE;
+ goto error;
if (lex_integer () < 1)
{
msg (SE, _("ID beginning column (%ld) must be positive."),
lex_integer ());
- return CMD_FAILURE;
+ goto error;
}
- rpd.id_beg = lex_integer ();
+ rpd->id_beg = lex_integer ();
lex_get ();
lex_negative_to_dash ();
if (lex_match ('-'))
{
if (!lex_force_int ())
- return CMD_FAILURE;
+ goto error;
if (lex_integer () < 1)
{
msg (SE, _("ID ending column (%ld) must be positive."),
lex_integer ());
- return CMD_FAILURE;
+ goto error;
}
- if (lex_integer () < rpd.id_end)
+ if (lex_integer () < rpd->id_end)
{
msg (SE, _("ID ending column (%ld) cannot be less than "
"ID beginning column (%d)."),
- lex_integer (), rpd.id_beg);
- return CMD_FAILURE;
+ lex_integer (), rpd->id_beg);
+ goto error;
}
- rpd.id_end = lex_integer ();
+ rpd->id_end = lex_integer ();
lex_get ();
}
- else rpd.id_end = rpd.id_beg;
+ else rpd->id_end = rpd->id_beg;
if (!lex_force_match ('='))
- return CMD_FAILURE;
- rpd.id_var = parse_variable ();
- if (rpd.id_var == NULL)
- return CMD_FAILURE;
+ goto error;
+ rpd->id_var = parse_variable ();
+ if (rpd->id_var == NULL)
+ goto error;
- find_variable_input_spec (rpd.id_var, &rpd.id_spec);
+ find_variable_input_spec (rpd->id_var, &rpd->id_spec);
+ rpd->id_value = xnmalloc (rpd->id_var->nv, sizeof *rpd->id_value);
}
else if (lex_match_id ("TABLE"))
table = 1;
else
{
lex_error (NULL);
- return CMD_FAILURE;
+ goto error;
}
if (!lex_force_match ('/'))
- return CMD_FAILURE;
+ goto error;
}
/* 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."));
- return CMD_FAILURE;
+ goto error;
}
/* Enforce ID restriction. */
- if ((seen & 16) && !(seen & 8))
+ if (saw_id && !saw_continued)
{
msg (SE, _("ID specified without CONTINUED."));
- return CMD_FAILURE;
+ goto error;
}
- /* Calculate starts_end, cont_end if necessary. */
- if (rpd.starts_end.num == 0 && rpd.starts_end.var == NULL)
- rpd.starts_end.num = fh_record_width (rpd.handle);
- if (rpd.cont_end.num == 0 && rpd.starts_end.var == NULL)
- rpd.cont_end.num = fh_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.spec; 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 ())
- return CMD_FAILURE;
+ 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 ();
+ dump_fixed_table (rpd->first, fh, rpd->last->rec);
- {
- struct repeating_data_trns *new_trns;
-
- rpd.h.proc = read_one_set_of_repetitions;
- rpd.h.free = destroy_dls;
-
- new_trns = xmalloc (sizeof *new_trns);
- memcpy (new_trns, &rpd, sizeof *new_trns);
- add_transformation ((struct trns_header *) new_trns);
- }
+ add_transformation (repeating_data_trns_proc, repeating_data_trns_free, rpd);
return lex_end_of_command ();
+
+ error:
+ repeating_data_trns_free (rpd);
+ return CMD_FAILURE;
}
-/* Because of the way that DATA LIST is structured, it's not trivial
- to determine what input format is associated with a given variable.
- This function finds the input format specification for variable V
- and puts it in SPEC. */
+/* Finds the input format specification for variable V and puts
+ it in SPEC. Because of the way that DATA LIST is structured,
+ this is nontrivial. */
static void
find_variable_input_spec (struct variable *v, struct fmt_spec *spec)
{
- int i;
+ size_t i;
for (i = 0; i < n_trns; i++)
{
- struct data_list_pgm *pgm = (struct data_list_pgm *) t_trns[i];
+ struct transformation *trns = &t_trns[i];
- if (pgm->h.proc == read_one_case)
+ if (trns->proc == data_list_trns_proc)
{
+ struct data_list_pgm *pgm = trns->private;
struct dls_var_spec *iter;
- for (iter = pgm->spec; iter; iter = iter->next)
+ for (iter = pgm->first; iter; iter = iter->next)
if (iter->v == v)
{
*spec = iter->input;
return 0;
}
}
- else if (lex_integer_p ())
+ else if (lex_is_integer ())
{
value->num = lex_integer ();
return 1;
}
-/* Parses data specifications for repeating data groups. Taken from
- parse_fixed(). Returns nonzero only if successful. */
+/* Parses data specifications for repeating data groups, adding
+ them to the linked list with head FIRST and tail LAST.
+ Returns nonzero only if successful. */
static int
-parse_repeating_data (void)
+parse_repeating_data (struct dls_var_spec **first, struct dls_var_spec **last)
{
- int i;
+ struct fixed_parsing_state fx;
+ size_t i;
fx.recno = 0;
fx.sc = 1;
while (token != '.')
{
- fx.spec.rec = fx.recno;
-
- if (!parse_DATA_LIST_vars (&fx.name, &fx.nname, PV_NONE))
+ 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 ())
+ if (!fixed_parse_compatible (&fx, first, last))
goto fail;
}
else if (token == '(')
{
- fx.level = 0;
- fx.cname = 0;
- if (!fixed_parse_fortran ())
+ if (!fixed_parse_fortran (&fx, first, last))
goto fail;
}
else
{
msg (SE, _("SPSS-like or FORTRAN-like format "
- "specification expected after variable names."));
+ "specification expected after variable names."));
goto fail;
}
- for (i = 0; i < fx.nname; i++)
+ for (i = 0; i < fx.name_cnt; i++)
free (fx.name[i]);
free (fx.name);
}
- if (token != '.')
- {
- lex_error (_("expecting end of command"));
- return 0;
- }
return 1;
-fail:
- for (i = 0; i < fx.nname; i++)
+ fail:
+ for (i = 0; i < fx.name_cnt; i++)
free (fx.name[i]);
free (fx.name);
return 0;
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;
}
-/* Parses one record of repeated data and outputs corresponding cases.
- Repeating data is present in line LINE having length LEN.
- Repeating data begins in column BEG and continues through column
- END inclusive (1-based columns); occurrences are offset OFS columns
- from each other. C is the case that will be filled in; T is the
- REPEATING DATA transformation. The record ID will be verified if
- COMPARE_ID is nonzero; if it is zero, then the record ID is
- initialized to the ID present in the case (assuming that ID
- location was specified by the user). Returns number of occurrences
- parsed up to the specified maximum of MAX_OCCURS. */
+/* Parameter record passed to rpd_parse_record(). */
+struct rpd_parse_info
+ {
+ struct repeating_data_trns *trns; /* REPEATING DATA transformation. */
+ const char *line; /* Line being parsed. */
+ size_t len; /* Line length. */
+ int beg, end; /* First and last column of first occurrence. */
+ int ofs; /* Column offset between repeated occurrences. */
+ struct ccase *c; /* Case to fill in. */
+ int verify_id; /* Zero to initialize ID, nonzero to verify it. */
+ int max_occurs; /* Max number of occurrences to parse. */
+ };
+
+/* Parses one record of repeated data and outputs corresponding
+ cases. Returns number of occurrences parsed up to the
+ maximum specified in INFO. */
static int
-rpd_parse_record (int beg, int end, int ofs, struct ccase *c,
- struct repeating_data_trns *t,
- char *line, int len, int compare_id, int max_occurs)
+rpd_parse_record (const struct rpd_parse_info *info)
{
+ struct repeating_data_trns *t = info->trns;
+ int cur = info->beg;
int occurrences;
- int cur = beg;
/* Handle record ID values. */
if (t->id_beg != 0)
{
- static union value comparator;
- union value v;
+ union value id_temp[MAX_ELEMS_PER_VALUE];
+ /* Parse record ID into V. */
{
struct data_in di;
- data_in_finite_line (&di, line, len, t->id_beg, t->id_end);
- di.v = &v;
+ data_in_finite_line (&di, info->line, info->len, t->id_beg, t->id_end);
+ di.v = info->verify_id ? id_temp : t->id_value;
di.flags = 0;
di.f1 = t->id_beg;
di.format = t->id_spec;
return 0;
}
- if (compare_id == 0)
- comparator = v;
- else if ((t->id_var->type == NUMERIC && comparator.f != v.f)
- || (t->id_var->type == ALPHA
- && strncmp (comparator.s, v.s, t->id_var->width)))
+ if (info->verify_id
+ && compare_values (id_temp, t->id_value, t->id_var->width) != 0)
{
- char comp_str [64];
- char v_str [64];
+ char expected_str [MAX_FORMATTED_LEN + 1];
+ char actual_str [MAX_FORMATTED_LEN + 1];
- if (!data_out (comp_str, &t->id_var->print, &comparator))
- comp_str[0] = 0;
- if (!data_out (v_str, &t->id_var->print, &v))
- v_str[0] = 0;
-
- comp_str[t->id_var->print.w] = v_str[t->id_var->print.w] = 0;
+ data_out (expected_str, &t->id_var->print, t->id_value);
+ expected_str[t->id_var->print.w] = '\0';
+
+ data_out (actual_str, &t->id_var->print, id_temp);
+ actual_str[t->id_var->print.w] = '\0';
tmsg (SE, RPD_ERR,
- _("Mismatched case ID (%s). Expected value was %s."),
- v_str, comp_str);
+ _("Encountered mismatched record ID \"%s\" expecting \"%s\"."),
+ actual_str, expected_str);
return 0;
}
{
int warned = 0;
- for (occurrences = 0; occurrences < max_occurs; )
+ for (occurrences = 0; occurrences < info->max_occurs; )
{
- if (cur + ofs > end + 1)
+ if (cur + info->ofs > info->end + 1)
break;
occurrences++;
{
- struct dls_var_spec *var_spec = t->spec;
+ struct dls_var_spec *var_spec = t->first;
for (; var_spec; var_spec = var_spec->next)
{
int fc = var_spec->fc - 1 + cur;
int lc = var_spec->lc - 1 + cur;
- if (fc > len && !warned && var_spec->input.type != FMT_A)
+ if (fc > info->len && !warned && var_spec->input.type != FMT_A)
{
warned = 1;
tmsg (SW, RPD_ERR,
- _("Variable %s startging in column %d extends "
+ _("Variable %s starting in column %d extends "
"beyond physical record length of %d."),
- var_spec->v->name, fc, len);
+ var_spec->v->name, fc, info->len);
}
{
struct data_in di;
- data_in_finite_line (&di, line, len, fc, lc);
- di.v = &c->data[var_spec->fv];
+ data_in_finite_line (&di, info->line, info->len, fc, lc);
+ di.v = case_data_rw (info->c, var_spec->fv);
di.flags = 0;
di.f1 = fc + 1;
di.format = var_spec->input;
}
}
- cur += ofs;
+ cur += info->ofs;
- if (!write_case ())
+ if (!t->write_case (t->wc_data))
return 0;
}
}
return occurrences;
}
-/* Analogous to read_one_case; reads one set of repetitions of the
- elements in the REPEATING DATA structure. Returns -1 on success,
- -2 on end of file or on failure. */
-static int
-read_one_set_of_repetitions (struct trns_header *trns, struct ccase *c)
+/* Reads one set of repetitions of the elements in the REPEATING
+ DATA structure. Returns -1 on success, -2 on end of file or
+ on failure. */
+int
+repeating_data_trns_proc (void *trns_, struct ccase *c, int case_num UNUSED)
{
- dfm_push (dlsp->handle);
-
- {
- struct repeating_data_trns *t = (struct repeating_data_trns *) trns;
+ struct repeating_data_trns *t = trns_;
- char *line; /* Current record. */
- int len; /* Length of current record. */
+ struct fixed_string line; /* Current record. */
- int starts_beg; /* Starting column. */
- int starts_end; /* Ending column. */
- int occurs; /* Number of repetitions. */
- int length; /* Length of each occurrence. */
- int cont_beg; /* Starting column for continuation lines. */
- int cont_end; /* Ending column for continuation lines. */
+ int starts_beg; /* Starting column. */
+ int starts_end; /* Ending column. */
+ int occurs; /* Number of repetitions. */
+ int length; /* Length of each occurrence. */
+ int cont_beg; /* Starting column for continuation lines. */
+ int cont_end; /* Ending column for continuation lines. */
- int occurs_left; /* Number of occurrences remaining. */
+ int occurs_left; /* Number of occurrences remaining. */
- int code; /* Return value from rpd_parse_record(). */
+ int code; /* Return value from rpd_parse_record(). */
- int skip_first_record = 0;
+ int skip_first_record = 0;
- /* Read the current record. */
- dfm_bkwd_record (dlsp->handle, 1);
- line = dfm_get_record (dlsp->handle, &len);
- if (line == NULL)
- return -2;
- dfm_fwd_record (dlsp->handle);
-
- /* Calculate occurs, length. */
- occurs_left = occurs = realize_value (&t->occurs, c);
- if (occurs <= 0)
- {
- tmsg (SE, RPD_ERR, _("Invalid value %d for OCCURS."), occurs);
- return -3;
- }
- starts_beg = realize_value (&t->starts_beg, c);
- if (starts_beg <= 0)
- {
- tmsg (SE, RPD_ERR, _("Beginning column for STARTS (%d) must be "
- "at least 1."),
- starts_beg);
- return -3;
- }
- starts_end = realize_value (&t->starts_end, c);
- if (starts_end < starts_beg)
- {
- tmsg (SE, RPD_ERR, _("Ending column for STARTS (%d) is less than "
- "beginning column (%d)."),
- starts_end, starts_beg);
- skip_first_record = 1;
- }
- length = realize_value (&t->length, c);
- if (length < 0)
- {
- tmsg (SE, RPD_ERR, _("Invalid value %d for LENGTH."), length);
- length = 1;
- occurs = occurs_left = 1;
- }
- cont_beg = realize_value (&t->cont_beg, c);
- if (cont_beg < 0)
- {
- tmsg (SE, RPD_ERR, _("Beginning column for CONTINUED (%d) must be "
- "at least 1."),
- cont_beg);
- return -2;
- }
- cont_end = realize_value (&t->cont_end, c);
- if (cont_end < cont_beg)
- {
- tmsg (SE, RPD_ERR, _("Ending column for CONTINUED (%d) is less than "
- "beginning column (%d)."),
- cont_end, cont_beg);
- return -2;
- }
+ dfm_push (t->reader);
+
+ /* Read the current record. */
+ dfm_reread_record (t->reader, 1);
+ dfm_expand_tabs (t->reader);
+ if (dfm_eof (t->reader))
+ return -2;
+ dfm_get_record (t->reader, &line);
+ dfm_forward_record (t->reader);
- /* Parse the first record. */
- if (!skip_first_record)
- {
- code = rpd_parse_record (starts_beg, starts_end, length, c, t, line,
- len, 0, occurs_left);
- if (!code)
- return -2;
- }
- else if (cont_beg == 0)
+ /* Calculate occurs, length. */
+ occurs_left = occurs = realize_value (&t->occurs, c);
+ if (occurs <= 0)
+ {
+ tmsg (SE, RPD_ERR, _("Invalid value %d for OCCURS."), occurs);
return -3;
+ }
+ starts_beg = realize_value (&t->starts_beg, c);
+ if (starts_beg <= 0)
+ {
+ tmsg (SE, RPD_ERR, _("Beginning column for STARTS (%d) must be "
+ "at least 1."),
+ starts_beg);
+ return -3;
+ }
+ starts_end = realize_value (&t->starts_end, c);
+ if (starts_end < starts_beg)
+ {
+ tmsg (SE, RPD_ERR, _("Ending column for STARTS (%d) is less than "
+ "beginning column (%d)."),
+ starts_end, starts_beg);
+ skip_first_record = 1;
+ }
+ length = realize_value (&t->length, c);
+ if (length < 0)
+ {
+ tmsg (SE, RPD_ERR, _("Invalid value %d for LENGTH."), length);
+ length = 1;
+ occurs = occurs_left = 1;
+ }
+ cont_beg = realize_value (&t->cont_beg, c);
+ if (cont_beg < 0)
+ {
+ tmsg (SE, RPD_ERR, _("Beginning column for CONTINUED (%d) must be "
+ "at least 1."),
+ cont_beg);
+ return -2;
+ }
+ cont_end = realize_value (&t->cont_end, c);
+ if (cont_end < cont_beg)
+ {
+ tmsg (SE, RPD_ERR, _("Ending column for CONTINUED (%d) is less than "
+ "beginning column (%d)."),
+ cont_end, cont_beg);
+ return -2;
+ }
- /* Make sure, if some occurrences are left, that we have
- continuation records. */
- occurs_left -= code;
- if (occurs_left != 0 && cont_beg == 0)
- {
- tmsg (SE, RPD_ERR,
- _("Number of repetitions specified on OCCURS (%d) "
- "exceed number of repetitions available in "
- "space on STARTS (%d), and CONTINUED not specified."),
- occurs, code);
- return -2;
- }
+ /* Parse the first record. */
+ if (!skip_first_record)
+ {
+ struct rpd_parse_info info;
+ info.trns = t;
+ info.line = ls_c_str (&line);
+ info.len = ls_length (&line);
+ info.beg = starts_beg;
+ info.end = starts_end;
+ info.ofs = length;
+ info.c = c;
+ info.verify_id = 0;
+ info.max_occurs = occurs_left;
+ code = rpd_parse_record (&info);
+ if (!code)
+ return -2;
+ occurs_left -= code;
+ }
+ else if (cont_beg == 0)
+ return -3;
- /* Go on to additional records. */
- while (occurs_left != 0)
- {
- assert (occurs_left >= 0);
+ /* Make sure, if some occurrences are left, that we have
+ continuation records. */
+ if (occurs_left > 0 && cont_beg == 0)
+ {
+ tmsg (SE, RPD_ERR,
+ _("Number of repetitions specified on OCCURS (%d) "
+ "exceed number of repetitions available in "
+ "space on STARTS (%d), and CONTINUED not specified."),
+ occurs, (starts_end - starts_beg + 1) / length);
+ return -2;
+ }
- /* Read in another record. */
- line = dfm_get_record (dlsp->handle, &len);
- if (line == NULL)
- {
- tmsg (SE, RPD_ERR,
- _("Unexpected end of file with %d repetitions "
- "remaining out of %d."),
- occurs_left, occurs);
- return -2;
- }
- dfm_fwd_record (dlsp->handle);
+ /* Go on to additional records. */
+ while (occurs_left != 0)
+ {
+ struct rpd_parse_info info;
- /* Parse this record. */
- code = rpd_parse_record (cont_beg, cont_end, length, c, t, line,
- len, 1, occurs_left);
- if (!code)
- return -2;
- occurs_left -= code;
- }
- }
+ assert (occurs_left >= 0);
+
+ /* Read in another record. */
+ if (dfm_eof (t->reader))
+ {
+ tmsg (SE, RPD_ERR,
+ _("Unexpected end of file with %d repetitions "
+ "remaining out of %d."),
+ occurs_left, occurs);
+ return -2;
+ }
+ dfm_expand_tabs (t->reader);
+ dfm_get_record (t->reader, &line);
+ dfm_forward_record (t->reader);
+
+ /* Parse this record. */
+ info.trns = t;
+ info.line = ls_c_str (&line);
+ info.len = ls_length (&line);
+ info.beg = cont_beg;
+ info.end = cont_end;
+ info.ofs = length;
+ info.c = c;
+ info.verify_id = 1;
+ info.max_occurs = occurs_left;
+ code = rpd_parse_record (&info);;
+ if (!code)
+ return -2;
+ occurs_left -= code;
+ }
- dfm_pop (dlsp->handle);
+ dfm_pop (t->reader);
/* FIXME: This is a kluge until we've implemented multiplexing of
transformations. */
return -3;
}
+
+/* Frees a REPEATING DATA transformation. */
+void
+repeating_data_trns_free (void *rpd_)
+{
+ struct repeating_data_trns *rpd = rpd_;
+
+ destroy_dls_var_spec (rpd->first);
+ dfm_close_reader (rpd->reader);
+ free (rpd->id_value);
+ free (rpd);
+}
+
+/* Lets repeating_data_trns_proc() know how to write the cases
+ that it composes. Not elegant. */
+void
+repeating_data_set_write_case (struct transformation *trns_,
+ write_case_func *write_case,
+ write_case_data wc_data)
+{
+ struct repeating_data_trns *t = trns_->private;
+
+ assert (trns_->proc == repeating_data_trns_proc);
+ t->write_case = write_case;
+ t->wc_data = wc_data;
+}