/* PSPP - a program for statistical analysis.
- Copyright (C) 1997-9, 2000, 2006, 2007, 2009, 2010, 2011 Free Software Foundation, Inc.
+ Copyright (C) 1997-9, 2000, 2006, 2007, 2009, 2010, 2011, 2012, 2013 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
struct data_list_trns
{
struct data_parser *parser; /* Parser. */
+ struct dictionary *dict; /* Dictionary. */
struct dfm_reader *reader; /* Data file reader. */
struct variable *end; /* Variable specified on END subcommand. */
};
static bool parse_free (struct lexer *, struct dictionary *,
struct pool *, struct data_parser *);
-static trns_free_func data_list_trns_free;
-static trns_proc_func data_list_trns_proc;
+static const struct trns_class data_list_trns_class;
int
cmd_data_list (struct lexer *lexer, struct dataset *ds)
{
- struct dictionary *dict;
- struct data_parser *parser;
- struct dfm_reader *reader;
+ struct dictionary *dict = (in_input_program ()
+ ? dataset_dict (ds)
+ : dict_create (get_default_encoding ()));
+ struct data_parser *parser = data_parser_create ();
+ struct dfm_reader *reader = NULL;
+
struct variable *end = NULL;
struct file_handle *fh = NULL;
- struct string encoding = DS_EMPTY_INITIALIZER;
- int table;
- enum data_parser_type type;
- bool has_type;
- struct pool *tmp_pool;
- bool ok;
+ char *encoding = NULL;
+ int encoding_start = 0, encoding_end = 0;
- dict = (in_input_program ()
- ? dataset_dict (ds)
- : dict_create (get_default_encoding ()));
- parser = data_parser_create (dict);
- reader = NULL;
+ int table = -1; /* Print table if nonzero, -1=undecided. */
- table = -1; /* Print table if nonzero, -1=undecided. */
- has_type = false;
+ bool has_type = false;
+ int end_start = 0, end_end = 0;
while (lex_token (lexer) != T_SLASH)
{
if (lex_match_id (lexer, "FILE"))
}
else if (lex_match_id (lexer, "ENCODING"))
{
+ encoding_start = lex_ofs (lexer) - 1;
lex_match (lexer, T_EQUALS);
if (!lex_force_string (lexer))
goto error;
- ds_init_substring (&encoding, lex_tokss (lexer));
+ free (encoding);
+ encoding = ss_xstrdup (lex_tokss (lexer));
+ encoding_end = lex_ofs (lexer);
lex_get (lexer);
}
else if (lex_match_id (lexer, "RECORDS"))
{
+ if (data_parser_get_records (parser) > 0)
+ {
+ lex_sbc_only_once (lexer, "RECORDS");
+ goto error;
+ }
lex_match (lexer, T_EQUALS);
lex_match (lexer, T_LPAREN);
- if (!lex_force_int (lexer))
+ if (!lex_force_int_range (lexer, "RECORDS", 0, INT_MAX))
goto error;
data_parser_set_records (parser, lex_integer (lexer));
lex_get (lexer);
else if (lex_match_id (lexer, "SKIP"))
{
lex_match (lexer, T_EQUALS);
- if (!lex_force_int (lexer))
+ if (!lex_force_int_range (lexer, "SKIP", 0, INT_MAX))
goto error;
data_parser_set_skip (parser, lex_integer (lexer));
lex_get (lexer);
{
if (!in_input_program ())
{
- msg (SE, _("The END subcommand may only be used within "
- "INPUT PROGRAM."));
+ lex_next_error (lexer, -1, -1,
+ _("The %s subcommand may only be used within %s."),
+ "END", "INPUT PROGRAM");
goto error;
}
if (end)
{
- msg (SE, _("The END subcommand may only be specified once."));
+ lex_sbc_only_once (lexer, "END");
goto error;
}
+ end_start = lex_ofs (lexer) - 1;
lex_match (lexer, T_EQUALS);
if (!lex_force_id (lexer))
goto error;
+ end_end = lex_ofs (lexer);
+
end = dict_lookup_var (dict, lex_tokcstr (lexer));
if (!end)
end = dict_create_var_assert (dict, lex_tokcstr (lexer), 0);
}
else
{
- lex_error (lexer, NULL);
+ lex_error_expecting (lexer, "FILE", "ENCODING", "RECORDS",
+ "SKIP", "END", "NOTABLE", "TABLE",
+ "FIXED", "FREE", "LIST");
goto error;
}
if (has_type)
{
- msg (SE, _("Only one of FIXED, FREE, or LIST may "
- "be specified."));
+ lex_next_error (lexer, -1, -1,
+ _("Only one of FIXED, FREE, or LIST may "
+ "be specified."));
goto error;
}
has_type = true;
{
struct string delims = DS_EMPTY_INITIALIZER;
- while (!lex_match (lexer, T_RPAREN))
+ do
{
int delim;
else
{
/* XXX should support multibyte UTF-8 characters */
- lex_error (lexer, NULL);
+ lex_error (lexer, _("Syntax error expecting TAB "
+ "or delimiter string."));
ds_destroy (&delims);
goto error;
}
lex_match (lexer, T_COMMA);
}
+ while (!lex_match (lexer, T_RPAREN));
data_parser_set_empty_line_has_field (parser, true);
data_parser_set_quotes (parser, ss_empty ());
data_parser_set_quotes (parser, ss_cstr ("'\""));
data_parser_set_soft_delimiters (parser,
ss_cstr (CC_SPACES));
- data_parser_set_hard_delimiters (parser, ss_cstr (","));
+ const char decimal = settings_get_fmt_settings ()->decimal;
+ data_parser_set_hard_delimiters (parser,
+ ss_buffer (",", (decimal == '.') ? 1 : 0));
}
}
}
else
{
- lex_error (lexer, NULL);
+ lex_error_expecting (lexer, "FILE", "ENCODING", "RECORDS",
+ "SKIP", "END", "NOTABLE", "TABLE",
+ "FIXED", "FREE", "LIST");
goto error;
}
}
- type = data_parser_get_type (parser);
- if (! ds_is_empty (&encoding) && NULL == fh)
- msg (MW, _("Encoding should not be specified for inline data. It will be "
- "ignored."));
+ if (!fh)
+ {
+ fh = fh_inline_file ();
- if (fh == NULL)
- fh = fh_inline_file ();
+ if (encoding)
+ lex_ofs_msg (lexer, SW, encoding_start, encoding_end,
+ _("Encoding should not be specified for inline data. "
+ "It will be ignored."));
+ }
fh_set_default_handle (fh);
+ enum data_parser_type type = data_parser_get_type (parser);
if (type != DP_FIXED && end != NULL)
{
- msg (SE, _("The END subcommand may be used only with DATA LIST FIXED."));
+ lex_ofs_error (lexer, end_start, end_end,
+ _("The %s subcommand may be used only with %s."),
+ "END", "DATA LIST FIXED");
goto error;
}
- tmp_pool = pool_create ();
- if (type == DP_FIXED)
- ok = parse_fixed (lexer, dict, tmp_pool, parser);
- else
- ok = parse_free (lexer, dict, tmp_pool, parser);
+ struct pool *tmp_pool = pool_create ();
+ bool ok = (type == DP_FIXED
+ ? parse_fixed (lexer, dict, tmp_pool, parser)
+ : parse_free (lexer, dict, tmp_pool, parser));
pool_destroy (tmp_pool);
if (!ok)
goto error;
-
- if (!data_parser_any_fields (parser))
- {
- msg (SE, _("At least one variable must be specified."));
- goto error;
- }
+ assert (data_parser_any_fields (parser));
if (lex_end_of_command (lexer) != CMD_SUCCESS)
goto error;
if (table)
data_parser_output_description (parser, fh);
- reader = dfm_open_reader (fh, lexer);
+ reader = dfm_open_reader (fh, lexer, encoding);
if (reader == NULL)
goto error;
if (in_input_program ())
{
struct data_list_trns *trns = xmalloc (sizeof *trns);
- trns->parser = parser;
- trns->reader = reader;
- trns->end = end;
- add_transformation (ds, data_list_trns_proc, data_list_trns_free, trns);
+ *trns = (struct data_list_trns) {
+ .parser = parser,
+ .dict = dict_ref (dict),
+ .reader = reader,
+ .end = end,
+ };
+ add_transformation (ds, &data_list_trns_class, trns);
}
else
- data_parser_make_active_file (parser, ds, reader, dict);
+ data_parser_make_active_file (parser, ds, reader, dict, NULL, NULL);
fh_unref (fh);
- ds_destroy (&encoding);
+ free (encoding);
+
+ data_list_seen ();
return CMD_SUCCESS;
error:
data_parser_destroy (parser);
if (!in_input_program ())
- dict_destroy (dict);
+ dict_unref (dict);
fh_unref (fh);
- ds_destroy (&encoding);
+ free (encoding);
return CMD_CASCADING_FAILURE;
}
\f
int record = 0;
int column = 1;
- while (lex_token (lexer) != T_ENDCMD)
+ do
{
- char **names;
- size_t name_cnt, name_idx;
- struct fmt_spec *formats, *f;
- size_t format_cnt;
-
/* Parse everything. */
- if (!parse_record_placement (lexer, &record, &column)
- || !parse_DATA_LIST_vars_pool (lexer, dict, tmp_pool,
- &names, &name_cnt, PV_NONE)
- || !parse_var_placements (lexer, tmp_pool, name_cnt, true,
- &formats, &format_cnt))
+ int records_start = lex_ofs (lexer);
+ if (!parse_record_placement (lexer, &record, &column))
+ return false;
+
+ int vars_start = lex_ofs (lexer);
+ char **names;
+ size_t n_names;
+ if (!parse_DATA_LIST_vars_pool (lexer, dict, tmp_pool,
+ &names, &n_names, PV_NONE))
return false;
+ int vars_end = lex_ofs (lexer) - 1;
+ struct fmt_spec *formats;
+ size_t n_formats;
+ if (!parse_var_placements (lexer, tmp_pool, n_names, FMT_FOR_INPUT,
+ &formats, &n_formats))
+ return false;
+ int placements_end = lex_ofs (lexer) - 1;
/* Create variables and var specs. */
- name_idx = 0;
- for (f = formats; f < &formats[format_cnt]; f++)
+ size_t name_idx = 0;
+ for (struct fmt_spec *f = formats; f < &formats[n_formats]; f++)
if (!execute_placement_format (f, &record, &column))
{
- char *name;
- int width;
- struct variable *v;
-
- name = names[name_idx++];
-
/* Create variable. */
- width = fmt_var_width (f);
- v = dict_create_var (dict, name, width);
+ const char *name = names[name_idx++];
+ int width = fmt_var_width (f);
+ struct variable *v = dict_create_var (dict, name, width);
if (v != NULL)
{
/* Success. */
- struct fmt_spec output = fmt_for_output_from_input (f);
+ struct fmt_spec output = fmt_for_output_from_input (
+ f, settings_get_fmt_settings ());
var_set_both_formats (v, &output);
}
else
created. */
if (!in_input_program ())
{
- msg (SE, _("%s is a duplicate variable name."), name);
+ lex_ofs_error (lexer, vars_start, vars_end,
+ _("%s is a duplicate variable name."), name);
return false;
}
v = dict_lookup_var_assert (dict, name);
if ((width != 0) != (var_get_width (v) != 0))
{
- msg (SE, _("There is already a variable %s of a "
- "different type."),
- name);
+ lex_ofs_error (lexer, vars_start, placements_end,
+ _("There is already a variable %s of a "
+ "different type."), name);
return false;
}
if (width != 0 && width != var_get_width (v))
{
- msg (SE, _("There is already a string variable %s of a "
- "different width."), name);
+ lex_ofs_error (lexer, vars_start, placements_end,
+ _("There is already a string variable %s of "
+ "a different width."), name);
return false;
}
}
if (max_records && record > max_records)
{
- msg (SE, _("Cannot place variable %s on record %d when "
- "RECORDS=%d is specified."),
- var_get_name (v), record,
- data_parser_get_records (parser));
+ lex_ofs_error (lexer, records_start, vars_end,
+ _("Cannot place variable %s on record %d when "
+ "RECORDS=%d is specified."),
+ var_get_name (v), record,
+ data_parser_get_records (parser));
+ return false;
}
data_parser_add_fixed_field (parser, f,
column += f->w;
}
- assert (name_idx == name_cnt);
+ assert (name_idx == n_names);
}
+ while (lex_token (lexer) != T_ENDCMD);
return true;
}
struct pool *tmp_pool, struct data_parser *parser)
{
lex_get (lexer);
- while (lex_token (lexer) != T_ENDCMD)
+ do
{
- struct fmt_spec input, output;
- char **name;
- size_t name_cnt;
- size_t i;
+ char **names;
+ size_t n_names;
+ int vars_start = lex_ofs (lexer);
if (!parse_DATA_LIST_vars_pool (lexer, dict, tmp_pool,
- &name, &name_cnt, PV_NONE))
+ &names, &n_names, PV_NONE))
return false;
+ int vars_end = lex_ofs (lexer) - 1;
+ struct fmt_spec input, output;
if (lex_match (lexer, T_LPAREN))
{
- if (!parse_format_specifier (lexer, &input)
- || !fmt_check_input (&input)
- || !lex_force_match (lexer, T_RPAREN))
+ char type[FMT_TYPE_LEN_MAX + 1];
+
+ if (!parse_abstract_format_specifier (lexer, type, &input.w,
+ &input.d))
+ return NULL;
+ if (!fmt_from_name (type, &input.type))
+ {
+ lex_next_error (lexer, -1, -1,
+ _("Unknown format type `%s'."), type);
+ return NULL;
+ }
+
+ /* If no width was included, use the minimum width for the type.
+ This isn't quite right, because DATETIME by itself seems to become
+ DATETIME20 (see bug #30690), whereas this will become
+ DATETIME17. The correct behavior is not documented. */
+ if (input.w == 0)
+ {
+ input.w = fmt_min_input_width (input.type);
+ input.d = 0;
+ }
+
+ char *error = fmt_check_input__ (&input);
+ if (error)
+ {
+ lex_next_error (lexer, -1, -1, "%s", error);
+ free (error);
+ return NULL;
+ }
+ if (!lex_force_match (lexer, T_RPAREN))
return NULL;
/* As a special case, N format is treated as F format
if (input.type == FMT_N)
input.type = FMT_F;
- output = fmt_for_output_from_input (&input);
+ output = fmt_for_output_from_input (&input,
+ settings_get_fmt_settings ());
}
else
{
output = *settings_get_format ();
}
- for (i = 0; i < name_cnt; i++)
+ for (size_t i = 0; i < n_names; i++)
{
- struct variable *v;
-
- v = dict_create_var (dict, name[i], fmt_var_width (&input));
- if (v == NULL)
+ struct variable *v = dict_create_var (dict, names[i],
+ fmt_var_width (&input));
+ if (!v)
{
- msg (SE, _("%s is a duplicate variable name."), name[i]);
+ lex_ofs_error (lexer, vars_start, vars_end,
+ _("%s is a duplicate variable name."), names[i]);
return false;
}
var_set_both_formats (v, &output);
var_get_name (v));
}
}
+ while (lex_token (lexer) != T_ENDCMD);
return true;
}
struct data_list_trns *trns = trns_;
data_parser_destroy (trns->parser);
dfm_close_reader (trns->reader);
+ dict_unref (trns->dict);
free (trns);
return true;
}
/* Handle DATA LIST transformation TRNS, parsing data into *C. */
-static int
+static enum trns_result
data_list_trns_proc (void *trns_, struct ccase **c, casenumber case_num UNUSED)
{
struct data_list_trns *trns = trns_;
- int retval;
+ enum trns_result retval;
*c = case_unshare (*c);
- if (data_parser_parse (trns->parser, trns->reader, *c))
+ if (data_parser_parse (trns->parser, trns->reader, trns->dict, *c))
retval = TRNS_CONTINUE;
else if (dfm_reader_error (trns->reader) || dfm_eof (trns->reader) > 1)
{
/* If there was an END subcommand handle it. */
if (trns->end != NULL)
{
- double *end = &case_data_rw (*c, trns->end)->f;
+ double *end = case_num_rw (*c, trns->end);
if (retval == TRNS_END_FILE)
{
*end = 1.0;
return retval;
}
-\f
+
+static const struct trns_class data_list_trns_class = {
+ .name = "DATA LIST",
+ .execute = data_list_trns_proc,
+ .destroy = data_list_trns_free,
+};