X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;ds=sidebyside;f=src%2Flanguage%2Fdata-io%2Fget.c;h=d06a8e6594fcfeffec9df1cd96eab3c93c4a054e;hb=92c09e564002d356d20fc1e2e131027ef89f6748;hp=789d259e6cbb1a3a197788ff92ed64b7d3f439e8;hpb=57b436a22d9ae0e395fb2e3ce101c2b5c2e6939e;p=pspp-builds.git diff --git a/src/language/data-io/get.c b/src/language/data-io/get.c index 789d259e..d06a8e65 100644 --- a/src/language/data-io/get.c +++ b/src/language/data-io/get.c @@ -1,6 +1,5 @@ /* PSPP - computes sample statistics. Copyright (C) 1997-9, 2000, 2006 Free Software Foundation, Inc. - Written by Ben Pfaff . This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -23,16 +22,14 @@ #include #include -#include -#include #include -#include -#include +#include +#include +#include #include #include #include #include -#include #include #include #include @@ -46,9 +43,9 @@ #include #include #include -#include #include #include +#include #include "gettext.h" #define _(msgid) gettext (msgid) @@ -60,7 +57,7 @@ static void map_case (const struct case_map *, const struct ccase *, struct ccase *); static void destroy_case_map (struct case_map *); -static bool parse_dict_trim (struct dictionary *); +static bool parse_dict_trim (struct lexer *, struct dictionary *); /* Reading system and portable files. */ @@ -71,49 +68,42 @@ enum reader_command IMPORT_CMD }; -/* Case reader input program. */ -struct case_reader_pgm - { - struct any_reader *reader; /* File reader. */ - struct case_map *map; /* Map from file dict to active file dict. */ - struct ccase bounce; /* Bounce buffer. */ - }; - -static const struct case_source_class case_reader_source_class; - -static void case_reader_pgm_free (struct case_reader_pgm *); +static void get_translate_case (const struct ccase *, struct ccase *, + void *map_); +static bool get_destroy_case_map (void *map_); /* Parses a GET or IMPORT command. */ static int -parse_read_command (enum reader_command type) +parse_read_command (struct lexer *lexer, struct dataset *ds, enum reader_command type) { - struct case_reader_pgm *pgm = NULL; + struct casereader *reader = NULL; struct file_handle *fh = NULL; struct dictionary *dict = NULL; + struct case_map *map = NULL; for (;;) { - lex_match ('/'); + lex_match (lexer, '/'); - if (lex_match_id ("FILE") || token == T_STRING) + if (lex_match_id (lexer, "FILE") || lex_token (lexer) == T_STRING) { - lex_match ('='); + lex_match (lexer, '='); - fh = fh_parse (FH_REF_FILE | FH_REF_SCRATCH); + fh = fh_parse (lexer, FH_REF_FILE | FH_REF_SCRATCH); if (fh == NULL) goto error; } - else if (type == IMPORT_CMD && lex_match_id ("TYPE")) + else if (type == IMPORT_CMD && lex_match_id (lexer, "TYPE")) { - lex_match ('='); + lex_match (lexer, '='); - if (lex_match_id ("COMM")) + if (lex_match_id (lexer, "COMM")) type = PFM_COMM; - else if (lex_match_id ("TAPE")) + else if (lex_match_id (lexer, "TAPE")) type = PFM_TAPE; else { - lex_error (_("expecting COMM or TAPE")); + lex_error (lexer, _("expecting COMM or TAPE")); goto error; } } @@ -123,119 +113,70 @@ parse_read_command (enum reader_command type) if (fh == NULL) { - lex_sbc_missing ("FILE"); + lex_sbc_missing (lexer, "FILE"); goto error; } - discard_variables (); - - pgm = xmalloc (sizeof *pgm); - pgm->reader = any_reader_open (fh, &dict); - pgm->map = NULL; - case_nullify (&pgm->bounce); - if (pgm->reader == NULL) + reader = any_reader_open (fh, &dict); + if (reader == NULL) goto error; - case_create (&pgm->bounce, dict_get_next_value_idx (dict)); - start_case_map (dict); - while (token != '.') + while (lex_token (lexer) != '.') { - lex_match ('/'); - if (!parse_dict_trim (dict)) + lex_match (lexer, '/'); + if (!parse_dict_trim (lexer, dict)) goto error; } - pgm->map = finish_case_map (dict); + map = finish_case_map (dict); + if (map != NULL) + reader = casereader_create_translator (reader, + dict_get_next_value_idx (dict), + get_translate_case, + get_destroy_case_map, + map); - dict_destroy (default_dict); - default_dict = dict; - - proc_set_source (create_case_source (&case_reader_source_class, pgm)); + proc_set_active_file (ds, reader, dict); return CMD_SUCCESS; error: - case_reader_pgm_free (pgm); + casereader_destroy (reader); if (dict != NULL) dict_destroy (dict); return CMD_CASCADING_FAILURE; } -/* Frees a struct case_reader_pgm. */ -static void -case_reader_pgm_free (struct case_reader_pgm *pgm) -{ - if (pgm != NULL) - { - any_reader_close (pgm->reader); - destroy_case_map (pgm->map); - case_destroy (&pgm->bounce); - free (pgm); - } -} - -/* Clears internal state related to case reader input procedure. */ static void -case_reader_source_destroy (struct case_source *source) +get_translate_case (const struct ccase *input, struct ccase *output, + void *map_) { - struct case_reader_pgm *pgm = source->aux; - case_reader_pgm_free (pgm); + struct case_map *map = map_; + map_case (map, input, output); } -/* Reads all the cases from the data file into C and passes them - to WRITE_CASE one by one, passing WC_DATA. - Returns true if successful, false if an I/O error occurred. */ static bool -case_reader_source_read (struct case_source *source, - struct ccase *c, - write_case_func *write_case, write_case_data wc_data) +get_destroy_case_map (void *map_) { - struct case_reader_pgm *pgm = source->aux; - bool ok = true; - - do - { - bool got_case; - if (pgm->map == NULL) - got_case = any_reader_read (pgm->reader, c); - else - { - got_case = any_reader_read (pgm->reader, &pgm->bounce); - if (got_case) - map_case (pgm->map, &pgm->bounce, c); - } - if (!got_case) - break; - - ok = write_case (wc_data); - } - while (ok); - - return ok && !any_reader_error (pgm->reader); + struct case_map *map = map_; + destroy_case_map (map); + return true; } - -static const struct case_source_class case_reader_source_class = - { - "case reader", - NULL, - case_reader_source_read, - case_reader_source_destroy, - }; /* GET. */ int -cmd_get (void) +cmd_get (struct lexer *lexer, struct dataset *ds) { - return parse_read_command (GET_CMD); + return parse_read_command (lexer, ds, GET_CMD); } /* IMPORT. */ int -cmd_import (void) +cmd_import (struct lexer *lexer, struct dataset *ds) { - return parse_read_command (IMPORT_CMD); + return parse_read_command (lexer, ds, IMPORT_CMD); } /* Writing system and portable files. */ @@ -254,30 +195,6 @@ enum command_type PROC_CMD /* Procedure. */ }; -/* File writer plus a case map. */ -struct case_writer - { - struct any_writer *writer; /* File writer. */ - struct case_map *map; /* Map to output file dictionary - (null pointer for identity mapping). */ - struct ccase bounce; /* Bounce buffer for mapping (if needed). */ - }; - -/* Destroys AW. */ -static bool -case_writer_destroy (struct case_writer *aw) -{ - bool ok = true; - if (aw != NULL) - { - ok = any_writer_close (aw->writer); - destroy_case_map (aw->map); - case_destroy (&aw->bounce); - free (aw); - } - return ok; -} - /* Parses SAVE or XSAVE or EXPORT or XEXPORT command. WRITER_TYPE identifies the type of file to write, and COMMAND_TYPE identifies the type of command. @@ -288,15 +205,17 @@ case_writer_destroy (struct case_writer *aw) included. On failure, returns a null pointer. */ -static struct case_writer * -parse_write_command (enum writer_type writer_type, +static struct casewriter * +parse_write_command (struct lexer *lexer, struct dataset *ds, + enum writer_type writer_type, enum command_type command_type, bool *retain_unselected) { /* Common data. */ struct file_handle *handle; /* Output file. */ struct dictionary *dict; /* Dictionary for output file. */ - struct case_writer *aw; /* Writer. */ + struct casewriter *writer; /* Writer. */ + struct case_map *map; /* Map from input data to data for writer. */ /* Common options. */ bool print_map; /* Print map? TODO. */ @@ -312,11 +231,9 @@ parse_write_command (enum writer_type writer_type, *retain_unselected = true; handle = NULL; - dict = dict_clone (default_dict); - aw = xmalloc (sizeof *aw); - aw->writer = NULL; - aw->map = NULL; - case_nullify (&aw->bounce); + dict = dict_clone (dataset_dict (ds)); + writer = NULL; + map = NULL; print_map = false; print_short_names = false; sysfile_opts = sfm_writer_default_options (); @@ -325,10 +242,10 @@ parse_write_command (enum writer_type writer_type, start_case_map (dict); dict_delete_scratch_vars (dict); - lex_match ('/'); + lex_match (lexer, '/'); for (;;) { - if (lex_match_id ("OUTFILE")) + if (lex_match_id (lexer, "OUTFILE")) { if (handle != NULL) { @@ -336,181 +253,166 @@ parse_write_command (enum writer_type writer_type, goto error; } - lex_match ('='); + lex_match (lexer, '='); - handle = fh_parse (FH_REF_FILE | FH_REF_SCRATCH); + handle = fh_parse (lexer, FH_REF_FILE | FH_REF_SCRATCH); if (handle == NULL) goto error; } - else if (lex_match_id ("NAMES")) + else if (lex_match_id (lexer, "NAMES")) print_short_names = true; - else if (lex_match_id ("PERMISSIONS")) + else if (lex_match_id (lexer, "PERMISSIONS")) { bool cw; - lex_match ('='); - if (lex_match_id ("READONLY")) + lex_match (lexer, '='); + if (lex_match_id (lexer, "READONLY")) cw = false; - else if (lex_match_id ("WRITEABLE")) + else if (lex_match_id (lexer, "WRITEABLE")) cw = true; else { - lex_error (_("expecting %s or %s"), "READONLY", "WRITEABLE"); + lex_error (lexer, _("expecting %s or %s"), "READONLY", "WRITEABLE"); goto error; } sysfile_opts.create_writeable = porfile_opts.create_writeable = cw; } - else if (command_type == PROC_CMD && lex_match_id ("UNSELECTED")) + else if (command_type == PROC_CMD && lex_match_id (lexer, "UNSELECTED")) { - lex_match ('='); - if (lex_match_id ("RETAIN")) + lex_match (lexer, '='); + if (lex_match_id (lexer, "RETAIN")) *retain_unselected = true; - else if (lex_match_id ("DELETE")) + else if (lex_match_id (lexer, "DELETE")) *retain_unselected = false; else { - lex_error (_("expecting %s or %s"), "RETAIN", "DELETE"); + lex_error (lexer, _("expecting %s or %s"), "RETAIN", "DELETE"); goto error; } } - else if (writer_type == SYSFILE_WRITER && lex_match_id ("COMPRESSED")) + else if (writer_type == SYSFILE_WRITER && lex_match_id (lexer, "COMPRESSED")) sysfile_opts.compress = true; - else if (writer_type == SYSFILE_WRITER && lex_match_id ("UNCOMPRESSED")) + else if (writer_type == SYSFILE_WRITER && lex_match_id (lexer, "UNCOMPRESSED")) sysfile_opts.compress = false; - else if (writer_type == SYSFILE_WRITER && lex_match_id ("VERSION")) + else if (writer_type == SYSFILE_WRITER && lex_match_id (lexer, "VERSION")) { - lex_match ('='); - if (!lex_force_int ()) + lex_match (lexer, '='); + if (!lex_force_int (lexer)) goto error; - sysfile_opts.version = lex_integer (); - lex_get (); + sysfile_opts.version = lex_integer (lexer); + lex_get (lexer); } - else if (writer_type == PORFILE_WRITER && lex_match_id ("TYPE")) + else if (writer_type == PORFILE_WRITER && lex_match_id (lexer, "TYPE")) { - lex_match ('='); - if (lex_match_id ("COMMUNICATIONS")) + lex_match (lexer, '='); + if (lex_match_id (lexer, "COMMUNICATIONS")) porfile_opts.type = PFM_COMM; - else if (lex_match_id ("TAPE")) + else if (lex_match_id (lexer, "TAPE")) porfile_opts.type = PFM_TAPE; else { - lex_error (_("expecting %s or %s"), "COMM", "TAPE"); + lex_error (lexer, _("expecting %s or %s"), "COMM", "TAPE"); goto error; } } - else if (writer_type == PORFILE_WRITER && lex_match_id ("DIGITS")) + else if (writer_type == PORFILE_WRITER && lex_match_id (lexer, "DIGITS")) { - lex_match ('='); - if (!lex_force_int ()) + lex_match (lexer, '='); + if (!lex_force_int (lexer)) goto error; - porfile_opts.digits = lex_integer (); - lex_get (); + porfile_opts.digits = lex_integer (lexer); + lex_get (lexer); } - else if (!parse_dict_trim (dict)) + else if (!parse_dict_trim (lexer, dict)) goto error; - if (!lex_match ('/')) + if (!lex_match (lexer, '/')) break; } - if (lex_end_of_command () != CMD_SUCCESS) + if (lex_end_of_command (lexer) != CMD_SUCCESS) goto error; if (handle == NULL) { - lex_sbc_missing ("OUTFILE"); + lex_sbc_missing (lexer, "OUTFILE"); goto error; } dict_compact_values (dict); - aw->map = finish_case_map (dict); - if (aw->map != NULL) - case_create (&aw->bounce, dict_get_next_value_idx (dict)); if (fh_get_referent (handle) == FH_REF_FILE) { switch (writer_type) { case SYSFILE_WRITER: - aw->writer = any_writer_from_sfm_writer ( - sfm_open_writer (handle, dict, sysfile_opts)); + writer = sfm_open_writer (handle, dict, sysfile_opts); break; case PORFILE_WRITER: - aw->writer = any_writer_from_pfm_writer ( - pfm_open_writer (handle, dict, porfile_opts)); + writer = pfm_open_writer (handle, dict, porfile_opts); break; } } else - aw->writer = any_writer_open (handle, dict); - if (aw->writer == NULL) + writer = any_writer_open (handle, dict); + if (writer == NULL) goto error; + + map = finish_case_map (dict); + if (map != NULL) + writer = casewriter_create_translator (writer, + get_translate_case, + get_destroy_case_map, + map); dict_destroy (dict); - return aw; + return writer; error: - case_writer_destroy (aw); + casewriter_destroy (writer); dict_destroy (dict); + destroy_case_map (map); return NULL; } - -/* Writes case C to writer AW. */ -static bool -case_writer_write_case (struct case_writer *aw, const struct ccase *c) -{ - if (aw->map != NULL) - { - map_case (aw->map, c, &aw->bounce); - c = &aw->bounce; - } - return any_writer_write (aw->writer, c); -} /* SAVE and EXPORT. */ -static bool output_proc (const struct ccase *, void *); - /* Parses and performs the SAVE or EXPORT procedure. */ static int -parse_output_proc (enum writer_type writer_type) +parse_output_proc (struct lexer *lexer, struct dataset *ds, enum writer_type writer_type) { bool retain_unselected; struct variable *saved_filter_variable; - struct case_writer *aw; + struct casewriter *output; bool ok; - aw = parse_write_command (writer_type, PROC_CMD, &retain_unselected); - if (aw == NULL) + output = parse_write_command (lexer, ds, writer_type, PROC_CMD, + &retain_unselected); + if (output == NULL) return CMD_CASCADING_FAILURE; - saved_filter_variable = dict_get_filter (default_dict); + saved_filter_variable = dict_get_filter (dataset_dict (ds)); if (retain_unselected) - dict_set_filter (default_dict, NULL); - ok = procedure (output_proc, aw); - dict_set_filter (default_dict, saved_filter_variable); + dict_set_filter (dataset_dict (ds), NULL); - case_writer_destroy (aw); - return ok ? CMD_SUCCESS : CMD_CASCADING_FAILURE; -} + casereader_transfer (proc_open (ds), output); + ok = casewriter_destroy (output); + ok = proc_commit (ds) && ok; -/* Writes case C to file. */ -static bool -output_proc (const struct ccase *c, void *aw_) -{ - struct case_writer *aw = aw_; - return case_writer_write_case (aw, c); + dict_set_filter (dataset_dict (ds), saved_filter_variable); + + return ok ? CMD_SUCCESS : CMD_CASCADING_FAILURE; } int -cmd_save (void) +cmd_save (struct lexer *lexer, struct dataset *ds) { - return parse_output_proc (SYSFILE_WRITER); + return parse_output_proc (lexer, ds, SYSFILE_WRITER); } int -cmd_export (void) +cmd_export (struct lexer *lexer, struct dataset *ds) { - return parse_output_proc (PORFILE_WRITER); + return parse_output_proc (lexer, ds, PORFILE_WRITER); } /* XSAVE and XEXPORT. */ @@ -518,7 +420,7 @@ cmd_export (void) /* Transformation. */ struct output_trns { - struct case_writer *aw; /* Writer. */ + struct casewriter *writer; /* Writer. */ }; static trns_proc_func output_trns_proc; @@ -526,26 +428,28 @@ static trns_free_func output_trns_free; /* Parses the XSAVE or XEXPORT transformation command. */ static int -parse_output_trns (enum writer_type writer_type) +parse_output_trns (struct lexer *lexer, struct dataset *ds, enum writer_type writer_type) { struct output_trns *t = xmalloc (sizeof *t); - t->aw = parse_write_command (writer_type, XFORM_CMD, NULL); - if (t->aw == NULL) + t->writer = parse_write_command (lexer, ds, writer_type, XFORM_CMD, NULL); + if (t->writer == NULL) { free (t); return CMD_CASCADING_FAILURE; } - add_transformation (output_trns_proc, output_trns_free, t); + add_transformation (ds, output_trns_proc, output_trns_free, t); return CMD_SUCCESS; } /* Writes case C to the system file specified on XSAVE or XEXPORT. */ static int -output_trns_proc (void *trns_, struct ccase *c, int case_num UNUSED) +output_trns_proc (void *trns_, struct ccase *c, casenumber case_num UNUSED) { struct output_trns *t = trns_; - case_writer_write_case (t->aw, c); + struct ccase tmp; + case_clone (&tmp, c); + casewriter_write (t->writer, &tmp); return TRNS_CONTINUE; } @@ -555,62 +459,57 @@ static bool output_trns_free (void *trns_) { struct output_trns *t = trns_; - bool ok = true; - - if (t != NULL) - { - ok = case_writer_destroy (t->aw); - free (t); - } + bool ok = casewriter_destroy (t->writer); + free (t); return ok; } /* XSAVE command. */ int -cmd_xsave (void) +cmd_xsave (struct lexer *lexer, struct dataset *ds) { - return parse_output_trns (SYSFILE_WRITER); + return parse_output_trns (lexer, ds, SYSFILE_WRITER); } /* XEXPORT command. */ int -cmd_xexport (void) +cmd_xexport (struct lexer *lexer, struct dataset *ds) { - return parse_output_trns (PORFILE_WRITER); + return parse_output_trns (lexer, ds, PORFILE_WRITER); } -static bool rename_variables (struct dictionary *dict); -static bool drop_variables (struct dictionary *dict); -static bool keep_variables (struct dictionary *dict); +static bool rename_variables (struct lexer *lexer, struct dictionary *dict); +static bool drop_variables (struct lexer *, struct dictionary *dict); +static bool keep_variables (struct lexer *, struct dictionary *dict); /* Commands that read and write system files share a great deal of common syntactic structure for rearranging and dropping variables. This function parses this syntax and modifies DICT appropriately. Returns true on success, false on failure. */ static bool -parse_dict_trim (struct dictionary *dict) +parse_dict_trim (struct lexer *lexer, struct dictionary *dict) { - if (lex_match_id ("MAP")) + if (lex_match_id (lexer, "MAP")) { /* FIXME. */ return true; } - else if (lex_match_id ("DROP")) - return drop_variables (dict); - else if (lex_match_id ("KEEP")) - return keep_variables (dict); - else if (lex_match_id ("RENAME")) - return rename_variables (dict); + else if (lex_match_id (lexer, "DROP")) + return drop_variables (lexer, dict); + else if (lex_match_id (lexer, "KEEP")) + return keep_variables (lexer, dict); + else if (lex_match_id (lexer, "RENAME")) + return rename_variables (lexer, dict); else { - lex_error (_("expecting a valid subcommand")); + lex_error (lexer, _("expecting a valid subcommand")); return false; } } /* Parses and performs the RENAME subcommand of GET and SAVE. */ static bool -rename_variables (struct dictionary *dict) +rename_variables (struct lexer *lexer, struct dictionary *dict) { size_t i; @@ -623,29 +522,30 @@ rename_variables (struct dictionary *dict) int group; - lex_match ('='); - if (token != '(') + lex_match (lexer, '='); + if (lex_token (lexer) != '(') { struct variable *v; - v = parse_dict_variable (dict); + v = parse_variable (lexer, dict); if (v == NULL) return 0; - if (!lex_force_match ('=') - || !lex_force_id ()) + if (!lex_force_match (lexer, '=') + || !lex_force_id (lexer)) return 0; - if (dict_lookup_var (dict, tokid) != NULL) + if (dict_lookup_var (dict, lex_tokid (lexer)) != NULL) { msg (SE, _("Cannot rename %s as %s because there already exists " "a variable named %s. To rename variables with " "overlapping names, use a single RENAME subcommand " "such as \"/RENAME (A=B)(B=C)(C=A)\", or equivalently, " - "\"/RENAME (A B C=B C A)\"."), v->name, tokid, tokid); + "\"/RENAME (A B C=B C A)\"."), + var_get_name (v), lex_tokid (lexer), lex_tokid (lexer)); return 0; } - dict_rename_var (dict, v, tokid); - lex_get (); + dict_rename_var (dict, v, lex_tokid (lexer)); + lex_get (lexer); return 1; } @@ -653,18 +553,18 @@ rename_variables (struct dictionary *dict) v = NULL; new_names = 0; group = 1; - while (lex_match ('(')) + while (lex_match (lexer, '(')) { size_t old_nv = nv; - if (!parse_variables (dict, &v, &nv, PV_NO_DUPLICATE | PV_APPEND)) + if (!parse_variables (lexer, dict, &v, &nv, PV_NO_DUPLICATE | PV_APPEND)) goto done; - if (!lex_match ('=')) + if (!lex_match (lexer, '=')) { msg (SE, _("`=' expected after variable list.")); goto done; } - if (!parse_DATA_LIST_vars (&new_names, &nn, PV_APPEND | PV_NO_SCRATCH)) + if (!parse_DATA_LIST_vars (lexer, &new_names, &nn, PV_APPEND | PV_NO_SCRATCH)) goto done; if (nn != nv) { @@ -674,7 +574,7 @@ rename_variables (struct dictionary *dict) (unsigned) (nv - old_nv), (unsigned) (nn - old_nv), group); goto done; } - if (!lex_force_match (')')) + if (!lex_force_match (lexer, ')')) goto done; group++; } @@ -698,13 +598,13 @@ rename_variables (struct dictionary *dict) /* Parses and performs the DROP subcommand of GET and SAVE. Returns true if successful, false on failure.*/ static bool -drop_variables (struct dictionary *dict) +drop_variables (struct lexer *lexer, struct dictionary *dict) { struct variable **v; size_t nv; - lex_match ('='); - if (!parse_variables (dict, &v, &nv, PV_NONE)) + lex_match (lexer, '='); + if (!parse_variables (lexer, dict, &v, &nv, PV_NONE)) return false; dict_delete_vars (dict, v, nv); free (v); @@ -720,14 +620,14 @@ drop_variables (struct dictionary *dict) /* Parses and performs the KEEP subcommand of GET and SAVE. Returns true if successful, false on failure.*/ static bool -keep_variables (struct dictionary *dict) +keep_variables (struct lexer *lexer, struct dictionary *dict) { struct variable **v; size_t nv; size_t i; - lex_match ('='); - if (!parse_variables (dict, &v, &nv, PV_NONE)) + lex_match (lexer, '='); + if (!parse_variables (lexer, dict, &v, &nv, PV_NONE)) return false; /* Move the specified variables to the beginning. */ @@ -759,10 +659,11 @@ struct mtf_file struct mtf_file *next_min; /* Next in the chain of minimums. */ int type; /* One of MTF_*. */ - struct variable **by; /* List of BY variables for this file. */ + const struct variable **by; /* List of BY variables for this file. */ struct file_handle *handle; /* File handle. */ - struct any_reader *reader; /* File reader. */ + struct casereader *reader; /* File reader. */ struct dictionary *dict; /* Dictionary from system file. */ + bool active_file; /* Active file? */ /* IN subcommand. */ char *in_name; /* Variable name. */ @@ -785,7 +686,7 @@ struct mtf_proc char first[LONG_NAME_LEN + 1], last[LONG_NAME_LEN + 1]; struct dictionary *dict; /* Dictionary of output file. */ - struct casefile *output; /* MATCH FILES output. */ + struct casewriter *output; /* MATCH FILES output. */ struct ccase mtf_case; /* Case used for output. */ unsigned seq_num; /* Have we initialized this variable? */ @@ -794,12 +695,12 @@ struct mtf_proc static bool mtf_free (struct mtf_proc *); static bool mtf_close_file (struct mtf_file *); +static bool mtf_close_all_files (struct mtf_proc *); static int mtf_merge_dictionary (struct dictionary *const, struct mtf_file *); +static bool mtf_read_records (struct mtf_proc *); static bool mtf_delete_file_in_place (struct mtf_proc *, struct mtf_file **); -static bool mtf_read_nonactive_records (void *); -static bool mtf_processing_finish (void *); -static bool mtf_processing (const struct ccase *, void *); +static bool mtf_processing (struct mtf_proc *); static char *var_type_description (struct variable *); @@ -808,7 +709,7 @@ static struct variable *get_master (struct variable *); /* Parse and execute the MATCH FILES command. */ int -cmd_match_files (void) +cmd_match_files (struct lexer *lexer, struct dataset *ds) { struct mtf_proc mtf; struct mtf_file *first_table = NULL; @@ -817,9 +718,8 @@ cmd_match_files (void) bool used_active_file = false; bool saw_table = false; bool saw_in = false; + bool open_active_file = false; - bool ok; - mtf.head = mtf.tail = NULL; mtf.by_cnt = 0; mtf.first[0] = '\0'; @@ -829,24 +729,25 @@ cmd_match_files (void) case_nullify (&mtf.mtf_case); mtf.seq_num = 0; mtf.seq_nums = NULL; - dict_set_case_limit (mtf.dict, dict_get_case_limit (default_dict)); + dict_set_case_limit (mtf.dict, dict_get_case_limit (dataset_dict (ds))); - lex_match ('/'); - while (token == T_ID - && (lex_id_match ("FILE", tokid) || lex_id_match ("TABLE", tokid))) + lex_match (lexer, '/'); + while (lex_token (lexer) == T_ID + && (lex_id_match (ss_cstr ("FILE"), ss_cstr (lex_tokid (lexer))) + || lex_id_match (ss_cstr ("TABLE"), ss_cstr (lex_tokid (lexer))))) { struct mtf_file *file = xmalloc (sizeof *file); - if (lex_match_id ("FILE")) + if (lex_match_id (lexer, "FILE")) file->type = MTF_FILE; - else if (lex_match_id ("TABLE")) + else if (lex_match_id (lexer, "TABLE")) { file->type = MTF_TABLE; saw_table = true; } else NOT_REACHED (); - lex_match ('='); + lex_match (lexer, '='); file->by = NULL; file->handle = NULL; @@ -854,6 +755,7 @@ cmd_match_files (void) file->dict = NULL; file->in_name = NULL; file->in_var = NULL; + file->active_file = false; case_nullify (&file->input); /* FILEs go first, then TABLEs. */ @@ -881,7 +783,7 @@ cmd_match_files (void) first_table->prev = file; } - if (lex_match ('*')) + if (lex_match (lexer, '*')) { file->handle = NULL; file->reader = NULL; @@ -894,46 +796,45 @@ cmd_match_files (void) } used_active_file = true; - if (!proc_has_source ()) + if (!proc_has_active_file (ds)) { msg (SE, _("Cannot specify the active file since no active " "file has been defined.")); goto error; } - if (proc_make_temporary_transformations_permanent ()) + if (proc_make_temporary_transformations_permanent (ds)) msg (SE, _("MATCH FILES may not be used after TEMPORARY when " "the active file is an input source. " "Temporary transformations will be made permanent.")); - file->dict = default_dict; + file->dict = dataset_dict (ds); + file->active_file = true; } else { - file->handle = fh_parse (FH_REF_FILE | FH_REF_SCRATCH); + file->handle = fh_parse (lexer, FH_REF_FILE | FH_REF_SCRATCH); if (file->handle == NULL) goto error; file->reader = any_reader_open (file->handle, &file->dict); if (file->reader == NULL) goto error; - - case_create (&file->input, dict_get_next_value_idx (file->dict)); } - while (lex_match ('/')) - if (lex_match_id ("RENAME")) + while (lex_match (lexer, '/')) + if (lex_match_id (lexer, "RENAME")) { - if (!rename_variables (file->dict)) + if (!rename_variables (lexer, file->dict)) goto error; } - else if (lex_match_id ("IN")) + else if (lex_match_id (lexer, "IN")) { - lex_match ('='); - if (token != T_ID) + lex_match (lexer, '='); + if (lex_token (lexer) != T_ID) { - lex_error (NULL); + lex_error (lexer, NULL); goto error; } @@ -943,19 +844,19 @@ cmd_match_files (void) "TABLE.")); goto error; } - file->in_name = xstrdup (tokid); - lex_get (); + file->in_name = xstrdup (lex_tokid (lexer)); + lex_get (lexer); saw_in = true; } mtf_merge_dictionary (mtf.dict, file); } - while (token != '.') + while (lex_token (lexer) != '.') { - if (lex_match (T_BY)) + if (lex_match (lexer, T_BY)) { - struct variable **by; + const struct variable **by; if (mtf.by_cnt) { @@ -963,8 +864,8 @@ cmd_match_files (void) goto error; } - lex_match ('='); - if (!parse_variables (mtf.dict, &by, &mtf.by_cnt, + lex_match (lexer, '='); + if (!parse_variables_const (lexer, mtf.dict, &by, &mtf.by_cnt, PV_NO_DUPLICATE | PV_NO_SCRATCH)) goto error; @@ -976,12 +877,13 @@ cmd_match_files (void) for (i = 0; i < mtf.by_cnt; i++) { - iter->by[i] = dict_lookup_var (iter->dict, by[i]->name); + iter->by[i] = dict_lookup_var (iter->dict, + var_get_name (by[i])); if (iter->by[i] == NULL) { msg (SE, _("File %s lacks BY variable %s."), iter->handle ? fh_get_name (iter->handle) : "*", - by[i]->name); + var_get_name (by[i])); free (by); goto error; } @@ -989,7 +891,7 @@ cmd_match_files (void) } free (by); } - else if (lex_match_id ("FIRST")) + else if (lex_match_id (lexer, "FIRST")) { if (mtf.first[0] != '\0') { @@ -997,13 +899,13 @@ cmd_match_files (void) goto error; } - lex_match ('='); - if (!lex_force_id ()) + lex_match (lexer, '='); + if (!lex_force_id (lexer)) goto error; - strcpy (mtf.first, tokid); - lex_get (); + strcpy (mtf.first, lex_tokid (lexer)); + lex_get (lexer); } - else if (lex_match_id ("LAST")) + else if (lex_match_id (lexer, "LAST")) { if (mtf.last[0] != '\0') { @@ -1011,35 +913,35 @@ cmd_match_files (void) goto error; } - lex_match ('='); - if (!lex_force_id ()) + lex_match (lexer, '='); + if (!lex_force_id (lexer)) goto error; - strcpy (mtf.last, tokid); - lex_get (); + strcpy (mtf.last, lex_tokid (lexer)); + lex_get (lexer); } - else if (lex_match_id ("MAP")) + else if (lex_match_id (lexer, "MAP")) { /* FIXME. */ } - else if (lex_match_id ("DROP")) + else if (lex_match_id (lexer, "DROP")) { - if (!drop_variables (mtf.dict)) + if (!drop_variables (lexer, mtf.dict)) goto error; } - else if (lex_match_id ("KEEP")) + else if (lex_match_id (lexer, "KEEP")) { - if (!keep_variables (mtf.dict)) + if (!keep_variables (lexer, mtf.dict)) goto error; } else { - lex_error (NULL); + lex_error (lexer, NULL); goto error; } - if (!lex_match ('/') && token != '.') + if (!lex_match (lexer, '/') && lex_token (lexer) != '.') { - lex_end_of_command (); + lex_end_of_command (lexer); goto error; } } @@ -1068,7 +970,7 @@ cmd_match_files (void) for (i = 0; i < dict_get_var_cnt (d); i++) { struct variable *v = dict_get_var (d, i); - struct variable *mv = dict_lookup_var (mtf.dict, v->name); + struct variable *mv = dict_lookup_var (mtf.dict, var_get_name (v)); if (mv != NULL) set_master (v, mv); } @@ -1078,16 +980,16 @@ cmd_match_files (void) for (iter = mtf.head; iter != NULL; iter = iter->next) if (iter->in_name != NULL) { + struct fmt_spec format = fmt_for_output (FMT_F, 1, 0); iter->in_var = dict_create_var (mtf.dict, iter->in_name, 0); if (iter->in_var == NULL) { msg (SE, _("IN variable name %s duplicates an " "existing variable name."), - iter->in_var->name); + var_get_name (iter->in_var)); goto error; } - iter->in_var->print = iter->in_var->write - = make_output_format (FMT_F, 1, 0); + var_set_both_formats (iter->in_var, &format); } /* MATCH FILES performs an n-way merge on all its input files. @@ -1114,94 +1016,56 @@ cmd_match_files (void) 7. Repeat from step 2. - Unfortunately, this algorithm can't be implemented in a - straightforward way because there's no function to read a - record from the active file. Instead, it has to be written - as a state machine. - FIXME: For merging large numbers of files (more than 10?) a better algorithm would use a heap for finding minimum values. */ - if (!used_active_file) - discard_variables (); + if (used_active_file) + { + proc_discard_output (ds); + for (iter = mtf.head; iter != NULL; iter = iter->next) + if (iter->reader == NULL) + iter->reader = proc_open (ds); + open_active_file = true; + } dict_compact_values (mtf.dict); - mtf.output = fastfile_create (dict_get_next_value_idx (mtf.dict)); + mtf.output = autopaging_writer_create (dict_get_next_value_idx (mtf.dict)); mtf.seq_nums = xcalloc (dict_get_var_cnt (mtf.dict), sizeof *mtf.seq_nums); case_create (&mtf.mtf_case, dict_get_next_value_idx (mtf.dict)); - if (!mtf_read_nonactive_records (&mtf)) + if (!mtf_read_records (&mtf)) + goto error; + while (mtf.head && mtf.head->type == MTF_FILE) + if (!mtf_processing (&mtf)) + goto error; + if (!mtf_close_all_files (&mtf)) goto error; + if (open_active_file) + proc_commit (ds); - if (used_active_file) - { - proc_set_sink (create_case_sink (&null_sink_class, default_dict, NULL)); - ok = procedure (mtf_processing, &mtf) && mtf_processing_finish (&mtf); - } - else - ok = mtf_processing_finish (&mtf); - - discard_variables (); - - dict_destroy (default_dict); - default_dict = mtf.dict; + proc_set_active_file (ds, casewriter_make_reader (mtf.output), mtf.dict); mtf.dict = NULL; - proc_set_source (storage_source_create (mtf.output)); mtf.output = NULL; - - if (!mtf_free (&mtf)) - ok = false; - return ok ? CMD_SUCCESS : CMD_CASCADING_FAILURE; - + + return mtf_free (&mtf) ? CMD_SUCCESS : CMD_CASCADING_FAILURE; + error: + if (open_active_file) + proc_commit (ds); mtf_free (&mtf); return CMD_CASCADING_FAILURE; } -/* Repeats 2...7 an arbitrary number of times. */ -static bool -mtf_processing_finish (void *mtf_) -{ - struct mtf_proc *mtf = mtf_; - struct mtf_file *iter; - - /* Find the active file and delete it. */ - for (iter = mtf->head; iter; iter = iter->next) - if (iter->handle == NULL) - { - if (!mtf_delete_file_in_place (mtf, &iter)) - NOT_REACHED (); - break; - } - - while (mtf->head && mtf->head->type == MTF_FILE) - if (!mtf_processing (NULL, mtf)) - return false; - - return true; -} - -/* Return a string in a static buffer describing V's variable type and - width. */ +/* Return a string in an allocated buffer describing V's variable + type and width. */ static char * var_type_description (struct variable *v) { - static char buf[2][32]; - static int x = 0; - char *s; - - x ^= 1; - s = buf[x]; - - if (v->type == NUMERIC) - strcpy (s, "numeric"); + if (var_is_numeric (v)) + return xstrdup ("numeric"); else - { - assert (v->type == ALPHA); - sprintf (s, "string with width %d", v->width); - } - return s; + return xasprintf ("string with width %d", var_get_width (v)); } /* Closes FILE and frees its associated data. @@ -1210,22 +1074,18 @@ var_type_description (struct variable *v) static bool mtf_close_file (struct mtf_file *file) { - bool ok = file->reader == NULL || !any_reader_error (file->reader); + bool ok = casereader_destroy (file->reader); free (file->by); - any_reader_close (file->reader); - if (file->handle != NULL) + if (!file->active_file) dict_destroy (file->dict); - case_destroy (&file->input); free (file->in_name); + case_destroy (&file->input); free (file); return ok; } -/* Free all the data for the MATCH FILES procedure. - Returns true if successful, false if an I/O error - occurred. */ static bool -mtf_free (struct mtf_proc *mtf) +mtf_close_all_files (struct mtf_proc *mtf) { struct mtf_file *iter, *next; bool ok = true; @@ -1237,9 +1097,22 @@ mtf_free (struct mtf_proc *mtf) if (!mtf_close_file (iter)) ok = false; } - - if (mtf->dict) - dict_destroy (mtf->dict); + mtf->head = NULL; + return ok; +} + +/* Free all the data for the MATCH FILES procedure. + Returns true if successful, false if an I/O error + occurred. */ +static bool +mtf_free (struct mtf_proc *mtf) +{ + bool ok; + + ok = mtf_close_all_files (mtf); + + casewriter_destroy (mtf->output); + dict_destroy (mtf->dict); case_destroy (&mtf->mtf_case); free (mtf->seq_nums); @@ -1266,40 +1139,41 @@ mtf_delete_file_in_place (struct mtf_proc *mtf, struct mtf_file **file) *file = f->next; if (f->in_var != NULL) - case_data_rw (&mtf->mtf_case, f->in_var->fv)->f = 0.; + case_data_rw (&mtf->mtf_case, f->in_var)->f = 0.; for (i = 0; i < dict_get_var_cnt (f->dict); i++) { struct variable *v = dict_get_var (f->dict, i); struct variable *mv = get_master (v); if (mv != NULL) { - union value *out = case_data_rw (&mtf->mtf_case, mv->fv); + union value *out = case_data_rw (&mtf->mtf_case, mv); - if (v->type == NUMERIC) + if (var_is_numeric (v)) out->f = SYSMIS; else - memset (out->s, ' ', v->width); + memset (out->s, ' ', var_get_width (v)); } } return mtf_close_file (f); } -/* Read a record from every input file except the active file. +/* Read a record from every input file. Returns true if successful, false if an I/O error occurred. */ static bool -mtf_read_nonactive_records (void *mtf_) +mtf_read_records (struct mtf_proc *mtf) { - struct mtf_proc *mtf = mtf_; struct mtf_file *iter, *next; bool ok = true; for (iter = mtf->head; ok && iter != NULL; iter = next) { next = iter->next; - if (iter->handle && !any_reader_read (iter->reader, &iter->input)) - if (!mtf_delete_file_in_place (mtf, &iter)) - ok = false; + if (!casereader_read (iter->reader, &iter->input)) + { + if (!mtf_delete_file_in_place (mtf, &iter)) + ok = false; + } } return ok; } @@ -1308,46 +1182,67 @@ mtf_read_nonactive_records (void *mtf_) if A == B, 1 if A > B. */ static inline int mtf_compare_BY_values (struct mtf_proc *mtf, - struct mtf_file *a, struct mtf_file *b, - const struct ccase *c) + struct mtf_file *a, struct mtf_file *b) { - const struct ccase *ca = case_is_null (&a->input) ? c : &a->input; - const struct ccase *cb = case_is_null (&b->input) ? c : &b->input; - assert ((a == NULL) + (b == NULL) + (c == NULL) <= 1); - return case_compare_2dict (ca, cb, a->by, b->by, mtf->by_cnt); + return case_compare_2dict (&a->input, &b->input, a->by, b->by, mtf->by_cnt); } /* Perform one iteration of steps 3...7 above. Returns true if successful, false if an I/O error occurred. */ static bool -mtf_processing (const struct ccase *c, void *mtf_) +mtf_processing (struct mtf_proc *mtf) { - struct mtf_proc *mtf = mtf_; - - /* Do we need another record from the active file? */ - bool read_active_file; + struct mtf_file *min_head, *min_tail; /* Files with minimum BY values. */ + struct mtf_file *max_head, *max_tail; /* Files with non-minimum BYs. */ + struct mtf_file *iter, *next; + struct ccase out_case; - assert (mtf->head != NULL); - if (mtf->head->type == MTF_TABLE) - return true; - - do + /* 3. Find the FILE input record(s) that have minimum BY + values. Store all the values from these input records into + the output record. */ + min_head = min_tail = mtf->head; + max_head = max_tail = NULL; + for (iter = mtf->head->next; iter && iter->type == MTF_FILE; + iter = iter->next) { - struct mtf_file *min_head, *min_tail; /* Files with minimum BY values. */ - struct mtf_file *max_head, *max_tail; /* Files with non-minimum BYs. */ - struct mtf_file *iter, *next; - - read_active_file = false; + int cmp = mtf_compare_BY_values (mtf, min_head, iter); + if (cmp < 0) + { + if (max_head) + max_tail = max_tail->next_min = iter; + else + max_head = max_tail = iter; + } + else if (cmp == 0) + min_tail = min_tail->next_min = iter; + else /* cmp > 0 */ + { + if (max_head) + { + max_tail->next_min = min_head; + max_tail = min_tail; + } + else + { + max_head = min_head; + max_tail = min_tail; + } + min_head = min_tail = iter; + } + } + + /* 4. For every TABLE, read another record as long as the BY + values on the TABLE's input record are less than the FILEs' + BY values. If an exact match is found, store all the values + from the TABLE input record into the output record. */ + for (; iter != NULL; iter = next) + { + assert (iter->type == MTF_TABLE); - /* 3. Find the FILE input record(s) that have minimum BY - values. Store all the values from these input records into - the output record. */ - min_head = min_tail = mtf->head; - max_head = max_tail = NULL; - for (iter = mtf->head->next; iter && iter->type == MTF_FILE; - iter = iter->next) + next = iter->next; + for (;;) { - int cmp = mtf_compare_BY_values (mtf, min_head, iter, c); + int cmp = mtf_compare_BY_values (mtf, min_head, iter); if (cmp < 0) { if (max_head) @@ -1355,140 +1250,95 @@ mtf_processing (const struct ccase *c, void *mtf_) else max_head = max_tail = iter; } - else if (cmp == 0) - min_tail = min_tail->next_min = iter; + else if (cmp == 0) + min_tail = min_tail->next_min = iter; else /* cmp > 0 */ { - if (max_head) - { - max_tail->next_min = min_head; - max_tail = min_tail; - } - else - { - max_head = min_head; - max_tail = min_tail; - } - min_head = min_tail = iter; + case_destroy (&iter->input); + if (casereader_read (iter->reader, &iter->input)) + continue; + if (!mtf_delete_file_in_place (mtf, &iter)) + return false; } + break; } - - /* 4. For every TABLE, read another record as long as the BY - values on the TABLE's input record are less than the FILEs' - BY values. If an exact match is found, store all the values - from the TABLE input record into the output record. */ - for (; iter != NULL; iter = next) - { - assert (iter->type == MTF_TABLE); - - next = iter->next; - for (;;) - { - int cmp = mtf_compare_BY_values (mtf, min_head, iter, c); - if (cmp < 0) - { - if (max_head) - max_tail = max_tail->next_min = iter; - else - max_head = max_tail = iter; - } - else if (cmp == 0) - min_tail = min_tail->next_min = iter; - else /* cmp > 0 */ - { - if (iter->handle == NULL) - return true; - if (any_reader_read (iter->reader, &iter->input)) - continue; - if (!mtf_delete_file_in_place (mtf, &iter)) - return false; - } - break; - } - } + } - /* Next sequence number. */ - mtf->seq_num++; + /* Next sequence number. */ + mtf->seq_num++; - /* Store data to all the records we are using. */ - if (min_tail) - min_tail->next_min = NULL; - for (iter = min_head; iter; iter = iter->next_min) - { - int i; + /* Store data to all the records we are using. */ + if (min_tail) + min_tail->next_min = NULL; + for (iter = min_head; iter; iter = iter->next_min) + { + int i; - for (i = 0; i < dict_get_var_cnt (iter->dict); i++) - { - struct variable *v = dict_get_var (iter->dict, i); - struct variable *mv = get_master (v); + for (i = 0; i < dict_get_var_cnt (iter->dict); i++) + { + struct variable *v = dict_get_var (iter->dict, i); + struct variable *mv = get_master (v); + size_t mv_index = mv ? var_get_dict_index (mv) : 0; - if (mv != NULL && mtf->seq_nums[mv->index] != mtf->seq_num) - { - const struct ccase *record - = case_is_null (&iter->input) ? c : &iter->input; - union value *out = case_data_rw (&mtf->mtf_case, mv->fv); - - mtf->seq_nums[mv->index] = mtf->seq_num; - if (v->type == NUMERIC) - out->f = case_num (record, v->fv); - else - memcpy (out->s, case_str (record, v->fv), v->width); - } - } - if (iter->in_var != NULL) - case_data_rw (&mtf->mtf_case, iter->in_var->fv)->f = 1.; + if (mv != NULL && mtf->seq_nums[mv_index] != mtf->seq_num) + { + union value *out = case_data_rw (&mtf->mtf_case, mv); - if (iter->type == MTF_FILE && iter->handle == NULL) - read_active_file = true; - } + mtf->seq_nums[mv_index] = mtf->seq_num; + if (var_is_numeric (v)) + out->f = case_num (&iter->input, v); + else + memcpy (out->s, case_str (&iter->input, v), var_get_width (v)); + } + } + if (iter->in_var != NULL) + case_data_rw (&mtf->mtf_case, iter->in_var)->f = 1.; + } - /* Store missing values to all the records we're not - using. */ - if (max_tail) - max_tail->next_min = NULL; - for (iter = max_head; iter; iter = iter->next_min) - { - int i; + /* Store missing values to all the records we're not using. */ + if (max_tail) + max_tail->next_min = NULL; + for (iter = max_head; iter; iter = iter->next_min) + { + int i; - for (i = 0; i < dict_get_var_cnt (iter->dict); i++) - { - struct variable *v = dict_get_var (iter->dict, i); - struct variable *mv = get_master (v); + for (i = 0; i < dict_get_var_cnt (iter->dict); i++) + { + struct variable *v = dict_get_var (iter->dict, i); + struct variable *mv = get_master (v); + size_t mv_index = mv ? var_get_dict_index (mv) : 0; - if (mv != NULL && mtf->seq_nums[mv->index] != mtf->seq_num) - { - union value *out = case_data_rw (&mtf->mtf_case, mv->fv); - mtf->seq_nums[mv->index] = mtf->seq_num; + if (mv != NULL && mtf->seq_nums[mv_index] != mtf->seq_num) + { + union value *out = case_data_rw (&mtf->mtf_case, mv); + mtf->seq_nums[mv_index] = mtf->seq_num; - if (v->type == NUMERIC) - out->f = SYSMIS; - else - memset (out->s, ' ', v->width); - } + if (var_is_numeric (v)) + out->f = SYSMIS; + else + memset (out->s, ' ', var_get_width (v)); } - if (iter->in_var != NULL) - case_data_rw (&mtf->mtf_case, iter->in_var->fv)->f = 0.; - } + } + if (iter->in_var != NULL) + case_data_rw (&mtf->mtf_case, iter->in_var)->f = 0.; + } - /* 5. Write the output record. */ - casefile_append (mtf->output, &mtf->mtf_case); + /* 5. Write the output record. */ + case_clone (&out_case, &mtf->mtf_case); + casewriter_write (mtf->output, &out_case); - /* 6. Read another record from each input file FILE and TABLE - that we stored values from above. If we come to the end of - one of the input files, remove it from the list of input - files. */ - for (iter = min_head; iter && iter->type == MTF_FILE; iter = next) - { - next = iter->next_min; - if (iter->reader != NULL - && !any_reader_read (iter->reader, &iter->input)) - if (!mtf_delete_file_in_place (mtf, &iter)) - return false; - } + /* 6. Read another record from each input file FILE and TABLE + that we stored values from above. If we come to the end of + one of the input files, remove it from the list of input + files. */ + for (iter = min_head; iter && iter->type == MTF_FILE; iter = next) + { + next = iter->next_min; + case_destroy (&iter->input); + if (!casereader_read (iter->reader, &iter->input)) + if (!mtf_delete_file_in_place (mtf, &iter)) + return false; } - while (!read_active_file - && mtf->head != NULL && mtf->head->type == MTF_FILE); - return true; } @@ -1511,13 +1361,7 @@ mtf_merge_dictionary (struct dictionary *const m, struct mtf_file *f) dict_set_documents (m, d_docs); else { - char *new_docs; - size_t new_len; - - new_len = strlen (m_docs) + strlen (d_docs); - new_docs = xmalloc (new_len + 1); - strcpy (new_docs, m_docs); - strcat (new_docs, d_docs); + char *new_docs = xasprintf ("%s%s", m_docs, d_docs); dict_set_documents (m, new_docs); free (new_docs); } @@ -1526,40 +1370,36 @@ mtf_merge_dictionary (struct dictionary *const m, struct mtf_file *f) for (i = 0; i < dict_get_var_cnt (d); i++) { struct variable *dv = dict_get_var (d, i); - struct variable *mv = dict_lookup_var (m, dv->name); + struct variable *mv = dict_lookup_var (m, var_get_name (dv)); - if (dict_class_from_id (dv->name) == DC_SCRATCH) + if (dict_class_from_id (var_get_name (dv)) == DC_SCRATCH) continue; if (mv != NULL) { - if (mv->width != dv->width) + if (var_get_width (mv) != var_get_width (dv)) { msg (SE, _("Variable %s in file %s (%s) has different " "type or width from the same variable in " "earlier file (%s)."), - dv->name, fh_get_name (f->handle), + var_get_name (dv), fh_get_name (f->handle), var_type_description (dv), var_type_description (mv)); return 0; } - if (dv->width == mv->width) + if (var_get_width (dv) == var_get_width (mv)) { - if (val_labs_count (dv->val_labs) - && !val_labs_count (mv->val_labs)) - { - val_labs_destroy (mv->val_labs); - mv->val_labs = val_labs_copy (dv->val_labs); - } - if (!mv_is_empty (&dv->miss) && mv_is_empty (&mv->miss)) - mv_copy (&mv->miss, &dv->miss); + if (var_has_value_labels (dv) && !var_has_value_labels (mv)) + var_set_value_labels (mv, var_get_value_labels (dv)); + if (var_has_missing_values (dv) && !var_has_missing_values (mv)) + var_set_missing_values (mv, var_get_missing_values (dv)); } - if (dv->label && !mv->label) - mv->label = xstrdup (dv->label); + if (var_get_label (dv) && !var_get_label (mv)) + var_set_label (mv, var_get_label (dv)); } else - mv = dict_clone_var_assert (m, dv, dv->name); + mv = dict_clone_var_assert (m, dv, var_get_name (dv)); } return 1; @@ -1577,10 +1417,8 @@ set_master (struct variable *v, struct variable *master) static struct variable * get_master (struct variable *v) { - return v->aux; + return var_get_aux (v); } - - /* Case map. @@ -1614,7 +1452,7 @@ start_case_map (struct dictionary *d) { struct variable *v = dict_get_var (d, i); int *src_fv = xmalloc (sizeof *src_fv); - *src_fv = v->fv; + *src_fv = var_get_case_index (v); var_attach_aux (v, src_fv, var_dtor_free); } } @@ -1645,16 +1483,17 @@ finish_case_map (struct dictionary *d) for (i = 0; i < var_cnt; i++) { struct variable *v = dict_get_var (d, i); + size_t value_cnt = var_get_value_cnt (v); int *src_fv = (int *) var_detach_aux (v); size_t idx; - if (v->fv != *src_fv) + if (var_get_case_index (v) != *src_fv) identity_map = 0; - for (idx = 0; idx < v->nv; idx++) + for (idx = 0; idx < value_cnt; idx++) { int src_idx = *src_fv + idx; - int dst_idx = v->fv + idx; + int dst_idx = var_get_case_index (v) + idx; assert (map->map[dst_idx] == -1); map->map[dst_idx] = src_idx; @@ -1681,16 +1520,11 @@ map_case (const struct case_map *map, { size_t dst_idx; - assert (map != NULL); - assert (src != NULL); - assert (dst != NULL); - assert (src != dst); - for (dst_idx = 0; dst_idx < map->value_cnt; dst_idx++) { int src_idx = map->map[dst_idx]; if (src_idx != -1) - *case_data_rw (dst, dst_idx) = *case_data (src, src_idx); + *case_data_rw_idx (dst, dst_idx) = *case_data_idx (src, src_idx); } }