+ free (t_trns);
+ t_trns=NULL;
+ m_trns = 0;
+}
+\f
+/* Creates a case source with class CLASS and auxiliary data AUX
+ and based on dictionary DICT. */
+struct case_source *
+create_case_source (const struct case_source_class *class,
+ void *aux)
+{
+ struct case_source *source = xmalloc (sizeof *source);
+ source->class = class;
+ source->aux = aux;
+ return source;
+}
+
+/* Destroys case source SOURCE. It is the caller's responsible to
+ call the source's destroy function, if any. */
+void
+free_case_source (struct case_source *source)
+{
+ if (source != NULL)
+ {
+ if (source->class->destroy != NULL)
+ source->class->destroy (source);
+ free (source);
+ }
+}
+
+/* Returns nonzero if a case source is "complex". */
+int
+case_source_is_complex (const struct case_source *source)
+{
+ return source != NULL && (source->class == &input_program_source_class
+ || source->class == &file_type_source_class);
+}
+
+/* Returns nonzero if CLASS is the class of SOURCE. */
+int
+case_source_is_class (const struct case_source *source,
+ const struct case_source_class *class)
+{
+ return source != NULL && source->class == class;
+}
+
+/* Creates a case sink to accept cases from the given DICT with
+ class CLASS and auxiliary data AUX. */
+struct case_sink *
+create_case_sink (const struct case_sink_class *class,
+ const struct dictionary *dict,
+ void *aux)
+{
+ struct case_sink *sink = xmalloc (sizeof *sink);
+ sink->class = class;
+ sink->value_cnt = dict_get_compacted_value_cnt (dict);
+ sink->aux = aux;
+ return sink;
+}
+
+/* Destroys case sink SINK. */
+void
+free_case_sink (struct case_sink *sink)
+{
+ if (sink != NULL)
+ {
+ if (sink->class->destroy != NULL)
+ sink->class->destroy (sink);
+ free (sink);
+ }
+}
+\f
+/* Represents auxiliary data for handling SPLIT FILE. */
+struct split_aux_data
+ {
+ size_t case_count; /* Number of cases so far. */
+ struct ccase prev_case; /* Data in previous case. */
+
+ /* Functions to call... */
+ void (*begin_func) (void *); /* ...before data. */
+ int (*proc_func) (struct ccase *, void *); /* ...with data. */
+ void (*end_func) (void *); /* ...after data. */
+ void *func_aux; /* Auxiliary data. */
+ };
+
+static int equal_splits (const struct ccase *, const struct ccase *);
+static int procedure_with_splits_callback (struct ccase *, void *);
+static void dump_splits (struct ccase *);
+
+/* Like procedure(), but it automatically breaks the case stream
+ into SPLIT FILE break groups. Before each group of cases with
+ identical SPLIT FILE variable values, BEGIN_FUNC is called.
+ Then PROC_FUNC is called with each case in the group.
+ END_FUNC is called when the group is finished. FUNC_AUX is
+ passed to each of the functions as auxiliary data.
+
+ If the active file is empty, none of BEGIN_FUNC, PROC_FUNC,
+ and END_FUNC will be called at all.
+
+ If SPLIT FILE is not in effect, then there is one break group
+ (if the active file is nonempty), and BEGIN_FUNC and END_FUNC
+ will be called once. */
+void
+procedure_with_splits (void (*begin_func) (void *aux),
+ int (*proc_func) (struct ccase *, void *aux),
+ void (*end_func) (void *aux),
+ void *func_aux)
+{
+ struct split_aux_data split_aux;
+
+ split_aux.case_count = 0;
+ case_nullify (&split_aux.prev_case);
+ split_aux.begin_func = begin_func;
+ split_aux.proc_func = proc_func;
+ split_aux.end_func = end_func;
+ split_aux.func_aux = func_aux;
+
+ open_active_file ();
+ internal_procedure (procedure_with_splits_callback, &split_aux);
+ if (split_aux.case_count > 0 && end_func != NULL)
+ end_func (func_aux);
+ close_active_file ();
+
+ case_destroy (&split_aux.prev_case);
+}
+
+/* procedure() callback used by procedure_with_splits(). */
+static int
+procedure_with_splits_callback (struct ccase *c, void *split_aux_)
+{
+ struct split_aux_data *split_aux = split_aux_;
+
+ /* Start a new series if needed. */
+ if (split_aux->case_count == 0
+ || !equal_splits (c, &split_aux->prev_case))