X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=src%2Flanguage%2Fdata-io%2Fdata-list.c;h=6d8e388a8ecaab01f93972ab5341e66ef9e52e8e;hb=a80a9e0debb7c19c41e2d4331e4a6d41a7ecf50d;hp=3b091404f80f491edfe04418cf7c6b6830a16e72;hpb=c9ba3a14c478461aac8305599c070824113299c2;p=pspp diff --git a/src/language/data-io/data-list.c b/src/language/data-io/data-list.c index 3b091404f8..6d8e388a8e 100644 --- a/src/language/data-io/data-list.c +++ b/src/language/data-io/data-list.c @@ -1,5 +1,5 @@ /* PSPP - a program for statistical analysis. - Copyright (C) 1997-9, 2000, 2006, 2007, 2009 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 @@ -22,33 +22,34 @@ #include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "xsize.h" -#include "xalloc.h" +#include "data/case.h" +#include "data/casereader.h" +#include "data/data-in.h" +#include "data/dataset.h" +#include "data/dictionary.h" +#include "data/format.h" +#include "data/settings.h" +#include "data/transformations.h" +#include "data/variable.h" +#include "language/command.h" +#include "language/data-io/data-parser.h" +#include "language/data-io/data-reader.h" +#include "language/data-io/file-handle.h" +#include "language/data-io/inpt-pgm.h" +#include "language/data-io/placement-parser.h" +#include "language/lexer/format-parser.h" +#include "language/lexer/lexer.h" +#include "language/lexer/variable-parser.h" +#include "libpspp/assertion.h" +#include "libpspp/compiler.h" +#include "libpspp/i18n.h" +#include "libpspp/message.h" +#include "libpspp/misc.h" +#include "libpspp/pool.h" +#include "libpspp/str.h" + +#include "gl/xsize.h" +#include "gl/xalloc.h" #include "gettext.h" #define _(msgid) gettext (msgid) @@ -57,6 +58,7 @@ 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. */ }; @@ -66,66 +68,70 @@ static bool parse_fixed (struct lexer *, struct dictionary *, 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 (); - parser = data_parser_create (); - 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; - while (lex_token (lexer) != '/') + int end_start = 0, end_end = 0; + while (lex_token (lexer) != T_SLASH) { if (lex_match_id (lexer, "FILE")) { - lex_match (lexer, '='); + lex_match (lexer, T_EQUALS); fh_unref (fh); - fh = fh_parse (lexer, FH_REF_FILE | FH_REF_INLINE); + fh = fh_parse (lexer, FH_REF_FILE | FH_REF_INLINE, NULL); if (fh == NULL) goto error; } else if (lex_match_id (lexer, "ENCODING")) { - lex_match (lexer, '='); + encoding_start = lex_ofs (lexer) - 1; + lex_match (lexer, T_EQUALS); if (!lex_force_string (lexer)) goto error; - ds_init_string (&encoding, lex_tokstr (lexer)); + free (encoding); + encoding = ss_xstrdup (lex_tokss (lexer)); + encoding_end = lex_ofs (lexer); lex_get (lexer); } else if (lex_match_id (lexer, "RECORDS")) { - lex_match (lexer, '='); - lex_match (lexer, '('); - if (!lex_force_int (lexer)) + 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_range (lexer, "RECORDS", 0, INT_MAX)) goto error; data_parser_set_records (parser, lex_integer (lexer)); lex_get (lexer); - lex_match (lexer, ')'); + lex_match (lexer, T_RPAREN); } else if (lex_match_id (lexer, "SKIP")) { - lex_match (lexer, '='); - if (!lex_force_int (lexer)) + lex_match (lexer, T_EQUALS); + if (!lex_force_int_range (lexer, "SKIP", 0, INT_MAX)) goto error; data_parser_set_skip (parser, lex_integer (lexer)); lex_get (lexer); @@ -134,22 +140,26 @@ cmd_data_list (struct lexer *lexer, struct dataset *ds) { 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; } - lex_match (lexer, '='); + end_start = lex_ofs (lexer) - 1; + lex_match (lexer, T_EQUALS); if (!lex_force_id (lexer)) goto error; - end = dict_lookup_var (dict, lex_tokid (lexer)); + end_end = lex_ofs (lexer); + + end = dict_lookup_var (dict, lex_tokcstr (lexer)); if (!end) - end = dict_create_var_assert (dict, lex_tokid (lexer), 0); + end = dict_create_var_assert (dict, lex_tokcstr (lexer), 0); lex_get (lexer); } else if (lex_match_id (lexer, "NOTABLE")) @@ -172,46 +182,52 @@ cmd_data_list (struct lexer *lexer, struct dataset *ds) } 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; if (data_parser_get_type (parser) == DP_DELIMITED) { - if (lex_match (lexer, '(')) + if (lex_match (lexer, T_LPAREN)) { struct string delims = DS_EMPTY_INITIALIZER; - while (!lex_match (lexer, ')')) + do { int delim; if (lex_match_id (lexer, "TAB")) delim = '\t'; - else if (lex_token (lexer) == T_STRING - && ds_length (lex_tokstr (lexer)) == 1) + else if (lex_is_string (lexer) + && ss_length (lex_tokss (lexer)) == 1) { - delim = ds_first (lex_tokstr (lexer)); + delim = ss_first (lex_tokss (lexer)); lex_get (lexer); } else { - lex_error (lexer, NULL); + /* XXX should support multibyte UTF-8 characters */ + lex_error (lexer, _("Syntax error expecting TAB " + "or delimiter string.")); ds_destroy (&delims); goto error; } - ds_put_char (&delims, delim); + ds_put_byte (&delims, delim); - lex_match (lexer, ','); + 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 ()); @@ -225,50 +241,49 @@ cmd_data_list (struct lexer *lexer, struct dataset *ds) 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)) + if (!fh) { - if ( NULL == fh) - msg (MW, _("Encoding should not be specified for inline data. It will be ignored.")); - else - dict_set_encoding (dict, ds_cstr (&encoding)); - } + 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; @@ -278,30 +293,37 @@ cmd_data_list (struct lexer *lexer, struct dataset *ds) 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); + 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); + free (encoding); return CMD_CASCADING_FAILURE; } @@ -319,39 +341,41 @@ parse_fixed (struct lexer *lexer, struct dictionary *dict, int record = 0; int column = 1; - while (lex_token (lexer) != '.') + 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, 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 @@ -363,32 +387,36 @@ parse_fixed (struct lexer *lexer, struct dictionary *dict, 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, @@ -397,8 +425,9 @@ parse_fixed (struct lexer *lexer, struct dictionary *dict, column += f->w; } - assert (name_idx == name_cnt); + assert (name_idx == n_names); } + while (lex_token (lexer) != T_ENDCMD); return true; } @@ -413,22 +442,50 @@ parse_free (struct lexer *lexer, struct dictionary *dict, struct pool *tmp_pool, struct data_parser *parser) { lex_get (lexer); - while (lex_token (lexer) != '.') + do { - struct fmt_spec input, output; - char **name; - size_t name_cnt; - size_t i; + char **names; + size_t n_names; - if (!parse_DATA_LIST_vars_pool (lexer, tmp_pool, - &name, &name_cnt, PV_NONE)) + int vars_start = lex_ofs (lexer); + if (!parse_DATA_LIST_vars_pool (lexer, dict, tmp_pool, + &names, &n_names, PV_NONE)) return false; + int vars_end = lex_ofs (lexer) - 1; - if (lex_match (lexer, '(')) + 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, ')')) + 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 @@ -436,23 +493,24 @@ parse_free (struct lexer *lexer, struct dictionary *dict, 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 { - lex_match (lexer, '*'); + lex_match (lexer, T_ASTERISK); input = fmt_for_input (FMT_F, 8, 0); 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); @@ -462,6 +520,7 @@ parse_free (struct lexer *lexer, struct dictionary *dict, var_get_name (v)); } } + while (lex_token (lexer) != T_ENDCMD); return true; } @@ -476,19 +535,20 @@ data_list_trns_free (void *trns_) 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) { @@ -502,7 +562,7 @@ data_list_trns_proc (void *trns_, struct ccase **c, casenumber case_num UNUSED) /* 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; @@ -514,4 +574,9 @@ data_list_trns_proc (void *trns_, struct ccase **c, casenumber case_num UNUSED) return retval; } - + +static const struct trns_class data_list_trns_class = { + .name = "DATA LIST", + .execute = data_list_trns_proc, + .destroy = data_list_trns_free, +};