1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 1997-9, 2000, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014 Free Software Foundation, Inc.
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>. */
21 #include "data/any-reader.h"
22 #include "data/case-matcher.h"
23 #include "data/case.h"
24 #include "data/casereader.h"
25 #include "data/casewriter.h"
26 #include "data/dataset.h"
27 #include "data/dictionary.h"
28 #include "data/format.h"
29 #include "data/subcase.h"
30 #include "data/variable.h"
31 #include "language/command.h"
32 #include "language/commands/file-handle.h"
33 #include "language/commands/trim.h"
34 #include "language/lexer/lexer.h"
35 #include "language/lexer/variable-parser.h"
36 #include "language/commands/sort-criteria.h"
37 #include "libpspp/assertion.h"
38 #include "libpspp/i18n.h"
39 #include "libpspp/message.h"
40 #include "libpspp/string-array.h"
41 #include "libpspp/taint.h"
42 #include "math/sort.h"
44 #include "gl/minmax.h"
45 #include "gl/xalloc.h"
48 #define _(msgid) gettext (msgid)
50 enum comb_command_type
60 COMB_FILE, /* Specified on FILE= subcommand. */
61 COMB_TABLE /* Specified on TABLE= subcommand. */
64 /* One FILE or TABLE subcommand. */
68 enum comb_file_type type; /* COMB_FILE or COMB_TABLE. */
69 int start_ofs, end_ofs; /* Lexer offsets. */
72 struct subcase by_vars; /* BY variables in this input file. */
73 struct subcase src, dst; /* Data to copy to output; where to put it. */
74 const struct missing_values **mv; /* Each variable's missing values. */
77 struct file_handle *handle; /* Input file handle. */
78 struct dictionary *dict; /* Input file dictionary. */
79 struct casereader *reader; /* Input data source. */
80 struct ccase *data; /* The current input case. */
81 bool is_minimal; /* Does 'data' have minimum BY values across
83 bool is_sorted; /* Is file presorted on the BY variables? */
88 struct variable *in_var;
93 struct comb_file *files; /* All the files being merged. */
94 size_t n_files; /* Number of files. */
96 struct dictionary *dict; /* Dictionary of output file. */
97 struct subcase by_vars; /* BY variables in the output. */
98 struct casewriter *output; /* Destination for output. */
101 size_t n_var_sources, allocated_var_sources;
103 struct case_matcher *matcher;
106 Only if "first" or "last" is nonnull are the remaining
108 struct variable *first; /* Variable specified on FIRST (if any). */
109 struct variable *last; /* Variable specified on LAST (if any). */
110 struct ccase *buffered_case; /* Case ready for output except that we don't
111 know the value for the LAST var yet. */
112 union value *prev_BY; /* Values of BY vars in buffered_case. */
115 static int combine_files (enum comb_command_type, struct lexer *,
117 static void free_comb_proc (struct comb_proc *);
119 static void close_all_comb_files (struct comb_proc *);
120 static bool merge_dictionary (struct comb_proc *, struct lexer *,
123 static void execute_update (struct comb_proc *);
124 static void execute_match_files (struct comb_proc *);
125 static void execute_add_files (struct comb_proc *);
127 static bool create_flag_var (struct lexer *lexer, const char *subcommand_name,
128 const char *var_name, int var_ofs,
129 struct dictionary *, struct variable **);
130 static void output_case (struct comb_proc *, struct ccase *, union value *by);
131 static void output_buffered_case (struct comb_proc *);
134 cmd_add_files (struct lexer *lexer, struct dataset *ds)
136 return combine_files (COMB_ADD, lexer, ds);
140 cmd_match_files (struct lexer *lexer, struct dataset *ds)
142 return combine_files (COMB_MATCH, lexer, ds);
146 cmd_update (struct lexer *lexer, struct dataset *ds)
148 return combine_files (COMB_UPDATE, lexer, ds);
152 combine_files (enum comb_command_type command,
153 struct lexer *lexer, struct dataset *ds)
155 struct comb_proc proc = {
156 .dict = dict_create (get_default_encoding ()),
160 bool saw_sort = false;
161 struct casereader *active_file = NULL;
163 char *first_name = NULL;
165 char *last_name = NULL;
168 struct taint *taint = NULL;
170 size_t table_idx = SIZE_MAX;
171 int sort_ofs = INT_MAX;
172 size_t allocated_files = 0;
174 dict_set_case_limit (proc.dict, dict_get_case_limit (dataset_dict (ds)));
176 lex_match (lexer, T_SLASH);
179 int start_ofs = lex_ofs (lexer);
180 enum comb_file_type type;
181 if (lex_match_id (lexer, "FILE"))
183 else if (command == COMB_MATCH && lex_match_id (lexer, "TABLE"))
186 table_idx = MIN (table_idx, proc.n_files);
190 lex_match (lexer, T_EQUALS);
192 if (proc.n_files >= allocated_files)
193 proc.files = x2nrealloc (proc.files, &allocated_files,
195 struct comb_file *file = &proc.files[proc.n_files++];
196 *file = (struct comb_file) {
198 .start_ofs = start_ofs,
202 if (lex_match (lexer, T_ASTERISK))
204 if (!dataset_has_source (ds))
206 lex_next_error (lexer, -1, -1,
207 _("Cannot specify the active dataset since none "
208 "has been defined."));
212 if (proc_make_temporary_transformations_permanent (ds))
213 lex_next_error (lexer, -1, -1,
214 _("This command may not be used after TEMPORARY "
215 "when the active dataset is an input source. "
216 "Temporary transformations will be made "
219 file->dict = dict_clone (dataset_dict (ds));
223 file->handle = fh_parse (lexer, FH_REF_FILE, dataset_session (ds));
224 if (file->handle == NULL)
227 file->reader = any_reader_open_and_decode (file->handle, NULL,
229 if (file->reader == NULL)
232 file->end_ofs = lex_ofs (lexer) - 1;
234 while (lex_match (lexer, T_SLASH))
235 if (lex_match_id (lexer, "RENAME"))
237 if (!parse_dict_rename (lexer, file->dict))
240 else if (lex_match_id (lexer, "IN"))
242 lex_match (lexer, T_EQUALS);
243 if (!lex_force_id (lexer))
248 lex_error (lexer, _("Multiple IN subcommands for a single FILE "
252 file->in_name = xstrdup (lex_tokcstr (lexer));
253 file->in_ofs = lex_ofs (lexer);
256 else if (lex_match_id (lexer, "SORT"))
258 file->is_sorted = false;
260 sort_ofs = MIN (sort_ofs, lex_ofs (lexer) - 1);
263 if (!merge_dictionary (&proc, lexer, file))
267 while (lex_token (lexer) != T_ENDCMD)
269 if (lex_match (lexer, T_BY))
273 lex_sbc_only_once (lexer, "BY");
278 lex_match (lexer, T_EQUALS);
280 const struct variable **by_vars;
281 if (!parse_sort_criteria (lexer, proc.dict, &proc.by_vars,
286 for (size_t i = 0; i < proc.n_files; i++)
288 struct comb_file *file = &proc.files[i];
289 for (size_t j = 0; j < subcase_get_n_fields (&proc.by_vars); j++)
291 const char *name = var_get_name (by_vars[j]);
292 struct variable *var = dict_lookup_var (file->dict, name);
294 subcase_add_var (&file->by_vars, var,
295 subcase_get_direction (&proc.by_vars, j));
299 = file->handle ? fh_get_name (file->handle) : "*";
300 lex_ofs_error (lexer, file->start_ofs, file->end_ofs,
301 _("File %s lacks BY variable %s."),
306 assert (!ok || subcase_conformable (&file->by_vars,
307 &proc.files[0].by_vars));
314 else if (command != COMB_UPDATE && lex_match_id (lexer, "FIRST"))
316 if (first_name != NULL)
318 lex_sbc_only_once (lexer, "FIRST");
322 lex_match (lexer, T_EQUALS);
323 if (!lex_force_id (lexer))
325 first_name = xstrdup (lex_tokcstr (lexer));
326 first_ofs = lex_ofs (lexer);
329 else if (command != COMB_UPDATE && lex_match_id (lexer, "LAST"))
331 if (last_name != NULL)
333 lex_sbc_only_once (lexer, "LAST");
337 lex_match (lexer, T_EQUALS);
338 if (!lex_force_id (lexer))
340 last_name = xstrdup (lex_tokcstr (lexer));
341 last_ofs = lex_ofs (lexer);
344 else if (lex_match_id (lexer, "MAP"))
348 else if (lex_match_id (lexer, "DROP"))
350 if (!parse_dict_drop (lexer, proc.dict))
353 else if (lex_match_id (lexer, "KEEP"))
355 if (!parse_dict_keep (lexer, proc.dict))
360 if (command == COMB_UPDATE)
361 lex_error_expecting (lexer, "BY", "MAP", "DROP", "KEEP");
363 lex_error_expecting (lexer, "BY", "FIRST", "LAST",
364 "MAP", "DROP", "KEEP");
368 if (!lex_match (lexer, T_SLASH) && lex_token (lexer) != T_ENDCMD)
370 lex_end_of_command (lexer);
377 if (command == COMB_UPDATE)
379 lex_sbc_missing (lexer, "BY");
382 if (table_idx != SIZE_MAX)
384 const struct comb_file *table = &proc.files[table_idx];
385 lex_ofs_error (lexer, table->start_ofs, table->end_ofs,
386 _("BY is required when %s is specified."), "TABLE");
391 lex_ofs_error (lexer, sort_ofs, sort_ofs,
392 _("BY is required when %s is specified."), "SORT");
397 /* Add IN, FIRST, and LAST variables to master dictionary. */
398 for (size_t i = 0; i < proc.n_files; i++)
400 struct comb_file *file = &proc.files[i];
401 if (!create_flag_var (lexer, "IN", file->in_name, file->in_ofs,
402 proc.dict, &file->in_var))
405 if (!create_flag_var (lexer, "FIRST", first_name, first_ofs, proc.dict, &proc.first)
406 || !create_flag_var (lexer, "LAST", last_name, last_ofs, proc.dict, &proc.last))
409 dict_delete_scratch_vars (proc.dict);
411 /* Set up mapping from each file's variables to master
413 for (size_t i = 0; i < proc.n_files; i++)
415 struct comb_file *file = &proc.files[i];
416 size_t src_n_vars = dict_get_n_vars (file->dict);
418 file->mv = xnmalloc (src_n_vars, sizeof *file->mv);
419 for (size_t j = 0; j < src_n_vars; j++)
421 struct variable *src_var = dict_get_var (file->dict, j);
422 struct variable *dst_var = dict_lookup_var (proc.dict,
423 var_get_name (src_var));
426 size_t n = subcase_get_n_fields (&file->src);
427 file->mv[n] = var_get_missing_values (src_var);
428 subcase_add_var (&file->src, src_var, SC_ASCEND);
429 subcase_add_var (&file->dst, dst_var, SC_ASCEND);
434 proc.output = autopaging_writer_create (dict_get_proto (proc.dict));
435 taint = taint_clone (casewriter_get_taint (proc.output));
437 /* Set up case matcher. */
438 proc.matcher = case_matcher_create ();
439 for (size_t i = 0; i < proc.n_files; i++)
441 struct comb_file *file = &proc.files[i];
442 if (file->reader == NULL)
444 if (active_file == NULL)
446 proc_discard_output (ds);
447 file->reader = active_file = proc_open_filtering (ds, false);
450 file->reader = casereader_clone (active_file);
452 if (!file->is_sorted)
453 file->reader = sort_execute (file->reader, &file->by_vars);
454 taint_propagate (casereader_get_taint (file->reader), taint);
455 file->data = casereader_read (file->reader);
456 if (file->type == COMB_FILE)
457 case_matcher_add_input (proc.matcher, &file->by_vars,
458 &file->data, &file->is_minimal);
461 if (command == COMB_ADD)
462 execute_add_files (&proc);
463 else if (command == COMB_MATCH)
464 execute_match_files (&proc);
465 else if (command == COMB_UPDATE)
466 execute_update (&proc);
470 case_matcher_destroy (proc.matcher);
472 close_all_comb_files (&proc);
473 if (active_file != NULL)
476 dataset_set_dict (ds, proc.dict);
477 dataset_set_source (ds, casewriter_make_reader (proc.output));
481 free_comb_proc (&proc);
486 return taint_destroy (taint) ? CMD_SUCCESS : CMD_CASCADING_FAILURE;
489 if (active_file != NULL)
491 free_comb_proc (&proc);
492 taint_destroy (taint);
495 return CMD_CASCADING_FAILURE;
498 /* Merge the dictionary for file F into master dictionary for PROC. */
500 merge_dictionary (struct comb_proc *proc, struct lexer *lexer,
503 struct dictionary *m = proc->dict;
504 struct dictionary *d = f->dict;
506 if (dict_get_label (m) == NULL)
507 dict_set_label (m, dict_get_label (d));
509 /* FIXME: If the input files have different encodings, then
510 the result is undefined.
511 The correct thing to do would be to convert to an encoding
512 which can cope with all the input files (eg UTF-8).
514 if (strcmp (dict_get_encoding (f->dict), dict_get_encoding (m)))
515 msg (MW, _("Combining files with incompatible encodings. String data may "
516 "not be represented correctly."));
518 const struct string_array *d_docs = dict_get_documents (d);
519 const struct string_array *m_docs = dict_get_documents (m);
523 dict_set_documents (m, d_docs);
526 size_t n = m_docs->n + d_docs->n;
527 struct string_array new_docs = {
528 .strings = xmalloc (n * sizeof *new_docs.strings),
530 for (size_t i = 0; i < m_docs->n; i++)
531 new_docs.strings[new_docs.n++] = m_docs->strings[i];
532 for (size_t i = 0; i < d_docs->n; i++)
533 new_docs.strings[new_docs.n++] = d_docs->strings[i];
535 dict_set_documents (m, &new_docs);
537 free (new_docs.strings);
541 for (size_t i = 0; i < dict_get_n_vars (d); i++)
543 struct variable *dv = dict_get_var (d, i);
544 struct variable *mv = dict_lookup_var (m, var_get_name (dv));
546 if (dict_class_from_id (var_get_name (dv)) == DC_SCRATCH)
551 mv = dict_clone_var_assert (m, dv);
552 if (proc->n_var_sources >= proc->allocated_var_sources)
553 proc->var_sources = x2nrealloc (proc->var_sources,
554 &proc->allocated_var_sources,
555 sizeof *proc->var_sources);
556 proc->var_sources[proc->n_var_sources++] = f - proc->files;
560 if (var_get_width (mv) != var_get_width (dv))
562 const char *var_name = var_get_name (dv);
563 msg (SE, _("Variable %s has different type or width in different "
564 "files."), var_name);
566 for (size_t j = 0; j < 2; j++)
568 const struct variable *ev = !j ? mv : dv;
569 const struct comb_file *ef
570 = !j ? &proc->files[proc->var_sources[var_get_dict_index (mv)]] : f;
571 const char *fn = ef->handle ? fh_get_name (ef->handle) : "*";
573 if (var_is_numeric (ev))
574 lex_ofs_msg (lexer, SN, ef->start_ofs, ef->end_ofs,
575 _("In file %s, %s is numeric."),
578 lex_ofs_msg (lexer, SN, ef->start_ofs, ef->end_ofs,
579 _("In file %s, %s is a string with width %d."),
580 fn, var_name, var_get_width (ev));
586 if (var_has_value_labels (dv) && !var_has_value_labels (mv))
587 var_set_value_labels (mv, var_get_value_labels (dv));
588 if (var_has_missing_values (dv) && !var_has_missing_values (mv))
589 var_set_missing_values (mv, var_get_missing_values (dv));
590 if (var_get_label (dv) && !var_get_label (mv))
591 var_set_label (mv, var_get_label (dv));
598 /* If VAR_NAME is non-NULL, attempts to create a
599 variable named VAR_NAME, with format F1.0, in DICT, and stores
600 a pointer to the variable in *VAR. Returns true if
601 successful, false if the variable name is a duplicate (in
602 which case a message saying that the variable specified on the
603 given SUBCOMMAND is a duplicate is emitted).
605 Does nothing and returns true if VAR_NAME is null. */
607 create_flag_var (struct lexer *lexer, const char *subcommand,
608 const char *var_name, int var_ofs,
609 struct dictionary *dict, struct variable **var)
611 if (var_name != NULL)
613 struct fmt_spec format = fmt_for_output (FMT_F, 1, 0);
614 *var = dict_create_var (dict, var_name, 0);
617 lex_ofs_error (lexer, var_ofs, var_ofs,
618 _("Variable name %s specified on %s subcommand "
619 "duplicates an existing variable name."),
620 var_name, subcommand);
623 var_set_both_formats (*var, format);
630 /* Closes all the files in PROC and frees their associated data. */
632 close_all_comb_files (struct comb_proc *proc)
634 for (size_t i = 0; i < proc->n_files; i++)
636 struct comb_file *file = &proc->files[i];
637 subcase_uninit (&file->by_vars);
638 subcase_uninit (&file->src);
639 subcase_uninit (&file->dst);
641 fh_unref (file->handle);
642 dict_unref (file->dict);
643 casereader_destroy (file->reader);
644 case_unref (file->data);
645 free (file->in_name);
652 /* Frees all the data for the procedure. */
654 free_comb_proc (struct comb_proc *proc)
656 close_all_comb_files (proc);
657 dict_unref (proc->dict);
658 casewriter_destroy (proc->output);
659 case_matcher_destroy (proc->matcher);
662 caseproto_destroy_values (subcase_get_proto (&proc->by_vars),
664 free (proc->prev_BY);
666 subcase_uninit (&proc->by_vars);
667 case_unref (proc->buffered_case);
668 free (proc->var_sources);
671 static bool scan_table (struct comb_file *, union value by[]);
672 static struct ccase *create_output_case (const struct comb_proc *);
673 static void apply_case (const struct comb_file *, struct ccase *);
674 static void apply_nonmissing_case (const struct comb_file *, struct ccase *);
675 static void advance_file (struct comb_file *, union value by[]);
676 static void output_case (struct comb_proc *, struct ccase *, union value by[]);
677 static void output_buffered_case (struct comb_proc *);
679 /* Executes the ADD FILES command. */
681 execute_add_files (struct comb_proc *proc)
685 while (case_matcher_match (proc->matcher, &by))
686 for (size_t i = 0; i < proc->n_files; i++)
688 struct comb_file *file = &proc->files[i];
689 while (file->is_minimal)
691 struct ccase *output = create_output_case (proc);
692 apply_case (file, output);
693 advance_file (file, by);
694 output_case (proc, output, by);
697 output_buffered_case (proc);
700 /* Executes the MATCH FILES command. */
702 execute_match_files (struct comb_proc *proc)
706 while (case_matcher_match (proc->matcher, &by))
708 struct ccase *output = create_output_case (proc);
709 for (size_t i = proc->n_files; i-- > 0;)
711 struct comb_file *file = &proc->files[i];
712 if (file->type == COMB_FILE)
714 if (file->is_minimal)
716 apply_case (file, output);
717 advance_file (file, NULL);
722 if (scan_table (file, by))
723 apply_case (file, output);
726 output_case (proc, output, by);
728 output_buffered_case (proc);
731 /* Executes the UPDATE command. */
733 execute_update (struct comb_proc *proc)
736 size_t n_duplicates = 0;
738 while (case_matcher_match (proc->matcher, &by))
740 struct comb_file *first, *file;
741 struct ccase *output;
743 /* Find first nonnull case in array and make an output case
745 output = create_output_case (proc);
746 for (first = &proc->files[0]; ; first++)
747 if (first->is_minimal)
749 apply_case (first, output);
750 advance_file (first, by);
752 /* Read additional cases and update the output case from
753 them. (Don't update the output case from any duplicate
754 cases in the master file.) */
755 for (file = first + (first == proc->files);
756 file < &proc->files[proc->n_files]; file++)
758 while (file->is_minimal)
760 apply_nonmissing_case (file, output);
761 advance_file (file, by);
764 casewriter_write (proc->output, output);
766 /* Write duplicate cases in the master file directly to the
768 if (first == proc->files && first->is_minimal)
771 while (first->is_minimal)
773 output = create_output_case (proc);
774 apply_case (first, output);
775 advance_file (first, by);
776 casewriter_write (proc->output, output);
782 msg (SW, _("Encountered %zu sets of duplicate cases in the master file."),
786 /* Reads FILE, which must be of type COMB_TABLE, until it
787 encounters a case with BY or greater for its BY variables.
788 Returns true if a case with exactly BY for its BY variables
789 was found, otherwise false. */
791 scan_table (struct comb_file *file, union value by[])
793 while (file->data != NULL)
795 int cmp = subcase_compare_3way_xc (&file->by_vars, by, file->data);
798 case_unref (file->data);
799 file->data = casereader_read (file->reader);
807 /* Creates and returns an output case for PROC, initializing each
808 of its values to system-missing or blanks, except that the
809 values of IN variables are set to 0. */
810 static struct ccase *
811 create_output_case (const struct comb_proc *proc)
813 size_t n_vars = dict_get_n_vars (proc->dict);
814 struct ccase *output = case_create (dict_get_proto (proc->dict));
815 for (size_t i = 0; i < n_vars; i++)
817 struct variable *v = dict_get_var (proc->dict, i);
818 value_set_missing (case_data_rw (output, v), var_get_width (v));
820 for (size_t i = 0; i < proc->n_files; i++)
822 struct comb_file *file = &proc->files[i];
823 if (file->in_var != NULL)
824 *case_num_rw (output, file->in_var) = false;
830 mark_file_used (const struct comb_file *file, struct ccase *output)
832 if (file->in_var != NULL)
833 *case_num_rw (output, file->in_var) = true;
836 /* Copies the data from FILE's case into output case OUTPUT.
837 If FILE has an IN variable, then it is set to 1 in OUTPUT. */
839 apply_case (const struct comb_file *file, struct ccase *output)
841 subcase_copy (&file->src, file->data, &file->dst, output);
842 mark_file_used (file, output);
845 /* Copies the data from FILE's case into output case OUTPUT,
846 skipping values that are missing or all spaces.
848 If FILE has an IN variable, then it is set to 1 in OUTPUT. */
850 apply_nonmissing_case (const struct comb_file *file, struct ccase *output)
852 for (size_t i = 0; i < subcase_get_n_fields (&file->src); i++)
854 const struct subcase_field *src_field = &file->src.fields[i];
855 const struct subcase_field *dst_field = &file->dst.fields[i];
856 const union value *src_value
857 = case_data_idx (file->data, src_field->case_index);
858 int width = src_field->width;
860 if (!mv_is_value_missing (file->mv[i], src_value)
861 && !(width > 0 && value_is_spaces (src_value, width)))
862 value_copy (case_data_rw_idx (output, dst_field->case_index),
865 mark_file_used (file, output);
868 /* Advances FILE to its next case. If BY is nonnull, then FILE's is_minimal
869 member is updated based on whether the new case's BY values still match
872 advance_file (struct comb_file *file, union value by[])
874 case_unref (file->data);
875 file->data = casereader_read (file->reader);
877 file->is_minimal = (file->data != NULL
878 && subcase_equal_cx (&file->by_vars, file->data, by));
881 /* Writes OUTPUT, whose BY values has been extracted into BY, to
882 PROC's output file, first initializing any FIRST or LAST
883 variables in OUTPUT to the correct values. */
885 output_case (struct comb_proc *proc, struct ccase *output, union value by[])
887 if (proc->first == NULL && proc->last == NULL)
888 casewriter_write (proc->output, output);
891 /* It's harder with LAST, because we can't know whether
892 this case is the last in a group until we've prepared
893 the *next* case also. Thus, we buffer the previous
894 output case until the next one is ready. */
896 if (proc->prev_BY != NULL)
898 new_BY = !subcase_equal_xx (&proc->by_vars, proc->prev_BY, by);
899 if (proc->last != NULL)
900 *case_num_rw (proc->buffered_case, proc->last) = new_BY;
901 casewriter_write (proc->output, proc->buffered_case);
906 proc->buffered_case = output;
907 if (proc->first != NULL)
908 *case_num_rw (proc->buffered_case, proc->first) = new_BY;
912 size_t n_values = subcase_get_n_fields (&proc->by_vars);
913 const struct caseproto *proto = subcase_get_proto (&proc->by_vars);
914 if (proc->prev_BY == NULL)
916 proc->prev_BY = xmalloc (n_values * sizeof *proc->prev_BY);
917 caseproto_init_values (proto, proc->prev_BY);
919 caseproto_copy (subcase_get_proto (&proc->by_vars), 0, n_values,
925 /* Writes a trailing buffered case to the output, if FIRST or
928 output_buffered_case (struct comb_proc *proc)
930 if (proc->prev_BY != NULL)
932 if (proc->last != NULL)
933 *case_num_rw (proc->buffered_case, proc->last) = 1.0;
934 casewriter_write (proc->output, proc->buffered_case);
935 proc->buffered_case = NULL;