+ proc_cancel_temporary_transformations ();
+}
+\f
+/* Returns the current set of permanent transformations,
+ and clears the permanent transformations.
+ For use by INPUT PROGRAM. */
+struct trns_chain *
+proc_capture_transformations (void)
+{
+ struct trns_chain *chain;
+
+ assert (temporary_trns_chain == NULL);
+ chain = permanent_trns_chain;
+ cur_trns_chain = permanent_trns_chain = trns_chain_create ();
+ return chain;
+}
+
+/* Adds a transformation that processes a case with PROC and
+ frees itself with FREE to the current set of transformations.
+ The functions are passed AUX as auxiliary data. */
+void
+add_transformation (trns_proc_func *proc, trns_free_func *free, void *aux)
+{
+ trns_chain_append (cur_trns_chain, NULL, proc, free, aux);
+}
+
+/* Adds a transformation that processes a case with PROC and
+ frees itself with FREE to the current set of transformations.
+ When parsing of the block of transformations is complete,
+ FINALIZE will be called.
+ The functions are passed AUX as auxiliary data. */
+void
+add_transformation_with_finalizer (trns_finalize_func *finalize,
+ trns_proc_func *proc,
+ trns_free_func *free, void *aux)
+{
+ trns_chain_append (cur_trns_chain, finalize, proc, free, aux);
+}
+
+/* Returns the index of the next transformation.
+ This value can be returned by a transformation procedure
+ function to indicate a "jump" to that transformation. */
+size_t
+next_transformation (void)
+{
+ return trns_chain_next (cur_trns_chain);
+}
+
+/* Returns true if the next call to add_transformation() will add
+ a temporary transformation, false if it will add a permanent
+ transformation. */
+bool
+proc_in_temporary_transformations (void)
+{
+ return temporary_trns_chain != NULL;
+}
+
+/* Marks the start of temporary transformations.
+ Further calls to add_transformation() will add temporary
+ transformations. */
+void
+proc_start_temporary_transformations (void)
+{
+ if (!proc_in_temporary_transformations ())
+ {
+ add_case_limit_trns ();
+
+ permanent_dict = dict_clone (default_dict);
+ trns_chain_finalize (permanent_trns_chain);
+ temporary_trns_chain = cur_trns_chain = trns_chain_create ();
+ }
+}
+
+/* Converts all the temporary transformations, if any, to
+ permanent transformations. Further transformations will be
+ permanent.
+ Returns true if anything changed, false otherwise. */
+bool
+proc_make_temporary_transformations_permanent (void)
+{
+ if (proc_in_temporary_transformations ())
+ {
+ trns_chain_finalize (temporary_trns_chain);
+ trns_chain_splice (permanent_trns_chain, temporary_trns_chain);
+ temporary_trns_chain = NULL;
+
+ dict_destroy (permanent_dict);
+ permanent_dict = NULL;
+
+ return true;
+ }
+ else
+ return false;
+}
+
+/* Cancels all temporary transformations, if any. Further
+ transformations will be permanent.
+ Returns true if anything changed, false otherwise. */
+bool
+proc_cancel_temporary_transformations (void)
+{
+ if (proc_in_temporary_transformations ())
+ {
+ dict_destroy (default_dict);
+ default_dict = permanent_dict;
+ permanent_dict = NULL;
+
+ trns_chain_destroy (temporary_trns_chain);
+ temporary_trns_chain = NULL;
+
+ return true;
+ }
+ else
+ return false;
+}
+
+/* Cancels all transformations, if any.
+ Returns true if successful, false on I/O error. */
+bool
+proc_cancel_all_transformations (void)
+{
+ bool ok;
+ ok = trns_chain_destroy (permanent_trns_chain);
+ ok = trns_chain_destroy (temporary_trns_chain) && ok;
+ permanent_trns_chain = cur_trns_chain = trns_chain_create ();
+ temporary_trns_chain = NULL;
+ return ok;
+}
+\f
+/* Initializes procedure handling. */
+void
+proc_init (void)
+{
+ default_dict = dict_create ();
+ proc_cancel_all_transformations ();
+}
+
+/* Finishes up procedure handling. */
+void
+proc_done (void)
+{
+ discard_variables ();
+}
+
+/* Sets SINK as the destination for procedure output from the
+ next procedure. */
+void
+proc_set_sink (struct case_sink *sink)
+{
+ assert (vfm_sink == NULL);
+ vfm_sink = sink;
+}
+
+/* Sets SOURCE as the source for procedure input for the next
+ procedure. */
+void
+proc_set_source (struct case_source *source)
+{
+ assert (vfm_source == NULL);
+ vfm_source = source;
+}
+
+/* Returns true if a source for the next procedure has been
+ configured, false otherwise. */
+bool
+proc_has_source (void)
+{
+ return vfm_source != NULL;
+}
+
+/* Returns the output from the previous procedure.
+ For use only immediately after executing a procedure.
+ The returned casefile is owned by the caller; it will not be
+ automatically used for the next procedure's input. */
+struct casefile *
+proc_capture_output (void)
+{
+ struct casefile *casefile;
+
+ /* Try to make sure that this function is called immediately
+ after procedure() or a similar function. */
+ assert (vfm_source != NULL);
+ assert (case_source_is_class (vfm_source, &storage_source_class));
+ assert (trns_chain_is_empty (permanent_trns_chain));
+ assert (!proc_in_temporary_transformations ());
+
+ casefile = storage_source_decapsulate (vfm_source);
+ vfm_source = NULL;
+
+ return casefile;
+}
+\f
+static trns_proc_func case_limit_trns_proc;
+static trns_free_func case_limit_trns_free;
+
+/* Adds a transformation that limits the number of cases that may
+ pass through, if default_dict has a case limit. */
+static void
+add_case_limit_trns (void)
+{
+ size_t case_limit = dict_get_case_limit (default_dict);
+ if (case_limit != 0)
+ {
+ size_t *cases_remaining = xmalloc (sizeof *cases_remaining);
+ *cases_remaining = case_limit;
+ add_transformation (case_limit_trns_proc, case_limit_trns_free,
+ cases_remaining);
+ dict_set_case_limit (default_dict, 0);
+ }
+}
+
+/* Limits the maximum number of cases processed to
+ *CASES_REMAINING. */
+static int
+case_limit_trns_proc (void *cases_remaining_,
+ struct ccase *c UNUSED, int case_nr UNUSED)
+{
+ size_t *cases_remaining = cases_remaining_;
+ if (*cases_remaining > 0)
+ {
+ *cases_remaining--;
+ return TRNS_CONTINUE;
+ }
+ else
+ return TRNS_DROP_CASE;
+}
+
+/* Frees the data associated with a case limit transformation. */
+static bool
+case_limit_trns_free (void *cases_remaining_)
+{
+ size_t *cases_remaining = cases_remaining_;
+ free (cases_remaining);
+ return true;
+}
+\f
+static trns_proc_func filter_trns_proc;
+
+/* Adds a temporary transformation to filter data according to
+ the variable specified on FILTER, if any. */
+static void
+add_filter_trns (void)
+{
+ struct variable *filter_var = dict_get_filter (default_dict);
+ if (filter_var != NULL)
+ {
+ proc_start_temporary_transformations ();
+ add_transformation (filter_trns_proc, NULL, filter_var);
+ }
+}
+
+/* FILTER transformation. */
+static int
+filter_trns_proc (void *filter_var_,
+ struct ccase *c UNUSED, int case_nr UNUSED)
+
+{
+ struct variable *filter_var = filter_var_;
+ double f = case_num (c, filter_var->fv);
+ return (f != 0.0 && !mv_is_num_missing (&filter_var->miss, f)
+ ? TRNS_CONTINUE : TRNS_DROP_CASE);
+}
+\f
+static trns_proc_func process_if_trns_proc;
+static trns_free_func process_if_trns_free;
+
+/* Adds a temporary transformation to filter data according to
+ the expression specified on PROCESS IF, if any. */
+static void
+add_process_if_trns (void)
+{
+ if (process_if_expr != NULL)
+ {
+ proc_start_temporary_transformations ();
+ add_transformation (process_if_trns_proc, process_if_trns_free,
+ process_if_expr);
+ process_if_expr = NULL;
+ }
+}
+
+/* PROCESS IF transformation. */
+static int
+process_if_trns_proc (void *expression_,
+ struct ccase *c UNUSED, int case_nr UNUSED)
+
+{
+ struct expression *expression = expression_;
+ return (expr_evaluate_num (expression, c, case_nr) == 1.0
+ ? TRNS_CONTINUE : TRNS_DROP_CASE);
+}
+
+/* Frees a PROCESS IF transformation. */
+static bool
+process_if_trns_free (void *expression_)
+{
+ struct expression *expression = expression_;
+ expr_free (expression);
+ return true;