+Thu May 4 21:47:48 2006 Ben Pfaff <blp@gnu.org>
+
+ Continue reforming procedure execution. In this phase, move
+ procedure.c and procedure.h from src to src/data. Update
+ makefiles and #includes accordingly.
+
+ * automake.mk: Remove special rule for src/procedure.o.
+
+ * procedure.c: Moved to src/data.
+
+ * procedure.h: Moved to src/data.
+
Wed May 3 22:24:34 2006 Ben Pfaff <blp@gnu.org>
Continue reforming procedure execution. In this phase, get rid of
endif
AM_CPPFLAGS += -DPKGDATADIR=\"$(pkgdatadir)\"
-
-src/procedure.o: AM_CPPFLAGS += \
- -I$(top_srcdir)/src/language \
- -I$(top_srcdir)/src/language/control \
- -I$(top_srcdir)/src/output \
- -I$(top_srcdir)/src/data \
- -I$(top_srcdir)/src/libpspp
-
-
+Thu May 4 21:50:11 2006 Ben Pfaff <blp@gnu.org>
+
+ Continue reforming procedure execution. In this phase, move
+ procedure.c and procedure.h from src to src/data. Update
+ makefiles and #includes accordingly.
+
+ * procedure.c: Moved here from src/.
+
+ * procedure.h: Moved here from src/.
+
Wed May 3 22:42:12 2006 Ben Pfaff <blp@gnu.org>
Continue reforming procedure execution. In this phase, get rid of
src/data/missing-values.h \
src/data/make-file.c \
src/data/make-file.h \
+ src/data/procedure.c \
+ src/data/procedure.h \
src/data/por-file-reader.c \
src/data/por-file-reader.h \
src/data/por-file-writer.c \
--- /dev/null
+/* PSPP - computes sample statistics.
+ Copyright (C) 1997-9, 2000, 2006 Free Software Foundation, Inc.
+ Written by Ben Pfaff <blp@gnu.org>.
+
+ 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 the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#include <config.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <data/case-source.h>
+#include <data/case-sink.h>
+#include <data/case.h>
+#include <data/casefile.h>
+#include <data/dictionary.h>
+#include <data/file-handle-def.h>
+#include <data/procedure.h>
+#include <data/settings.h>
+#include <data/storage-stream.h>
+#include <data/transformations.h>
+#include <data/value-labels.h>
+#include <data/variable.h>
+#include <language/expressions/public.h>
+#include <libpspp/alloc.h>
+#include <libpspp/message.h>
+#include <libpspp/misc.h>
+#include <libpspp/str.h>
+#include <output/manager.h>
+#include <output/table.h>
+
+#include "gettext.h"
+#define _(msgid) gettext (msgid)
+
+/*
+ Virtual File Manager (vfm):
+
+ vfm is used to process data files. It uses the model that
+ data is read from one stream (the data source), processed,
+ then written to another (the data sink). The data source is
+ then deleted and the data sink becomes the data source for the
+ next procedure. */
+
+/* Procedure execution data. */
+struct write_case_data
+ {
+ /* Function to call for each case. */
+ bool (*proc_func) (struct ccase *, void *); /* Function. */
+ void *aux; /* Auxiliary data. */
+
+ struct ccase trns_case; /* Case used for transformations. */
+ struct ccase sink_case; /* Case written to sink, if
+ compaction is necessary. */
+ size_t cases_written; /* Cases output so far. */
+ size_t cases_analyzed; /* Cases passed to procedure so far. */
+ };
+
+/* Cases are read from vfm_source,
+ pass through permanent_trns_chain (which transforms them into
+ the format described by permanent_dict),
+ are written to vfm_sink,
+ pass through temporary_trns_chain (which transforms them into
+ the format described by default_dict),
+ and are finally passed to the procedure. */
+static struct case_source *vfm_source;
+static struct trns_chain *permanent_trns_chain;
+static struct dictionary *permanent_dict;
+static struct case_sink *vfm_sink;
+static struct trns_chain *temporary_trns_chain;
+struct dictionary *default_dict;
+
+/* The transformation chain that the next transformation will be
+ added to. */
+static struct trns_chain *cur_trns_chain;
+
+/* The compactor used to compact a case, if necessary;
+ otherwise a null pointer. */
+static struct dict_compactor *compactor;
+
+/* Time at which vfm was last invoked. */
+static time_t last_vfm_invocation;
+
+/* Lag queue. */
+int n_lag; /* Number of cases to lag. */
+static int lag_count; /* Number of cases in lag_queue so far. */
+static int lag_head; /* Index where next case will be added. */
+static struct ccase *lag_queue; /* Array of n_lag ccase * elements. */
+
+static void add_case_limit_trns (void);
+static void add_filter_trns (void);
+static void add_process_if_trns (void);
+
+static bool internal_procedure (bool (*proc_func) (struct ccase *, void *),
+ void *aux);
+static void update_last_vfm_invocation (void);
+static void create_trns_case (struct ccase *, struct dictionary *);
+static void open_active_file (void);
+static bool write_case (struct write_case_data *wc_data);
+static void lag_case (const struct ccase *c);
+static void clear_case (struct ccase *c);
+static bool close_active_file (void);
+\f
+/* Public functions. */
+
+/* Returns the last time the data was read. */
+time_t
+time_of_last_procedure (void)
+{
+ if (last_vfm_invocation == 0)
+ update_last_vfm_invocation ();
+ return last_vfm_invocation;
+}
+
+/* Reads the data from the input program and writes it to a new
+ active file. For each case we read from the input program, we
+ do the following:
+
+ 1. Execute permanent transformations. If these drop the case,
+ start the next case from step 1.
+
+ 2. Write case to replacement active file.
+
+ 3. Execute temporary transformations. If these drop the case,
+ start the next case from step 1.
+
+ 4. Pass case to PROC_FUNC, passing AUX as auxiliary data.
+
+ Returns true if successful, false if an I/O error occurred. */
+bool
+procedure (bool (*proc_func) (struct ccase *, void *), void *aux)
+{
+ if (proc_func == NULL
+ && case_source_is_class (vfm_source, &storage_source_class)
+ && vfm_sink == NULL
+ && temporary_trns_chain == NULL
+ && trns_chain_is_empty (permanent_trns_chain))
+ {
+ expr_free (process_if_expr);
+ process_if_expr = NULL;
+ dict_set_case_limit (default_dict, 0);
+ dict_clear_vectors (default_dict);
+
+ update_last_vfm_invocation ();
+ return true;
+ }
+ else
+ {
+ bool ok;
+
+ open_active_file ();
+ ok = internal_procedure (proc_func, aux);
+ ok = close_active_file () && ok;
+
+ return ok;
+ }
+}
+
+/* Callback function for multipass_procedure(). */
+static bool
+multipass_callback (struct ccase *c, void *cf_)
+{
+ struct casefile *cf = cf_;
+ return casefile_append (cf, c);
+}
+
+/* Procedure that allows multiple passes over the input data.
+ The entire active file is passed to PROC_FUNC, with the given
+ AUX as auxiliary data, as a unit. */
+bool
+multipass_procedure (bool (*proc_func) (const struct casefile *, void *aux),
+ void *aux)
+{
+ if (case_source_is_class (vfm_source, &storage_source_class)
+ && vfm_sink == NULL
+ && temporary_trns_chain == NULL
+ && trns_chain_is_empty (permanent_trns_chain))
+ {
+ proc_func (storage_source_get_casefile (vfm_source), aux);
+
+ expr_free (process_if_expr);
+ process_if_expr = NULL;
+ dict_set_case_limit (default_dict, 0);
+ dict_clear_vectors (default_dict);
+
+ update_last_vfm_invocation ();
+ return true;
+ }
+ else
+ {
+ struct casefile *cf;
+ bool ok;
+
+ assert (proc_func != NULL);
+
+ cf = casefile_create (dict_get_next_value_idx (default_dict));
+
+ open_active_file ();
+ ok = internal_procedure (multipass_callback, cf);
+ ok = proc_func (cf, aux) && ok;
+ ok = close_active_file () && ok;
+
+ casefile_destroy (cf);
+
+ return ok;
+ }
+}
+
+/* Executes a procedure, as procedure(), except that the caller
+ is responsible for calling open_active_file() and
+ close_active_file().
+ Returns true if successful, false if an I/O error occurred. */
+static bool
+internal_procedure (bool (*proc_func) (struct ccase *, void *), void *aux)
+{
+ struct write_case_data wc_data;
+ bool ok;
+
+ wc_data.proc_func = proc_func;
+ wc_data.aux = aux;
+ create_trns_case (&wc_data.trns_case, default_dict);
+ case_create (&wc_data.sink_case, dict_get_next_value_idx (default_dict));
+ wc_data.cases_written = 0;
+
+ update_last_vfm_invocation ();
+
+ ok = (vfm_source == NULL
+ || vfm_source->class->read (vfm_source,
+ &wc_data.trns_case,
+ write_case, &wc_data));
+
+ case_destroy (&wc_data.sink_case);
+ case_destroy (&wc_data.trns_case);
+
+ return ok;
+}
+
+/* Updates last_vfm_invocation. */
+static void
+update_last_vfm_invocation (void)
+{
+ last_vfm_invocation = time (NULL);
+}
+
+/* Creates and returns a case, initializing it from the vectors
+ that say which `value's need to be initialized just once, and
+ which ones need to be re-initialized before every case. */
+static void
+create_trns_case (struct ccase *trns_case, struct dictionary *dict)
+{
+ size_t var_cnt = dict_get_var_cnt (dict);
+ size_t i;
+
+ case_create (trns_case, dict_get_next_value_idx (dict));
+ for (i = 0; i < var_cnt; i++)
+ {
+ struct variable *v = dict_get_var (dict, i);
+ union value *value = case_data_rw (trns_case, v->fv);
+
+ if (v->type == NUMERIC)
+ value->f = v->leave ? 0.0 : SYSMIS;
+ else
+ memset (value->s, ' ', v->width);
+ }
+}
+
+/* Makes all preparations for reading from the data source and writing
+ to the data sink. */
+static void
+open_active_file (void)
+{
+ add_case_limit_trns ();
+ add_filter_trns ();
+ add_process_if_trns ();
+
+ /* Finalize transformations. */
+ trns_chain_finalize (cur_trns_chain);
+
+ /* Make permanent_dict refer to the dictionary right before
+ data reaches the sink. */
+ if (permanent_dict == NULL)
+ permanent_dict = default_dict;
+
+ /* Figure out compaction. */
+ compactor = (dict_needs_compaction (permanent_dict)
+ ? dict_make_compactor (permanent_dict)
+ : NULL);
+
+ /* Prepare sink. */
+ if (vfm_sink == NULL)
+ vfm_sink = create_case_sink (&storage_sink_class, permanent_dict, NULL);
+ if (vfm_sink->class->open != NULL)
+ vfm_sink->class->open (vfm_sink);
+
+ /* Allocate memory for lag queue. */
+ if (n_lag > 0)
+ {
+ int i;
+
+ lag_count = 0;
+ lag_head = 0;
+ lag_queue = xnmalloc (n_lag, sizeof *lag_queue);
+ for (i = 0; i < n_lag; i++)
+ case_nullify (&lag_queue[i]);
+ }
+}
+
+/* Transforms trns_case and writes it to the replacement active
+ file if advisable. Returns true if more cases can be
+ accepted, false otherwise. Do not call this function again
+ after it has returned false once. */
+static bool
+write_case (struct write_case_data *wc_data)
+{
+ enum trns_result retval;
+ size_t case_nr;
+
+ /* Execute permanent transformations. */
+ case_nr = wc_data->cases_written + 1;
+ retval = trns_chain_execute (permanent_trns_chain,
+ &wc_data->trns_case, &case_nr);
+ if (retval != TRNS_CONTINUE)
+ goto done;
+
+ /* Write case to LAG queue. */
+ if (n_lag)
+ lag_case (&wc_data->trns_case);
+
+ /* Write case to replacement active file. */
+ wc_data->cases_written++;
+ if (vfm_sink->class->write != NULL)
+ {
+ if (compactor != NULL)
+ {
+ dict_compactor_compact (compactor, &wc_data->sink_case,
+ &wc_data->trns_case);
+ vfm_sink->class->write (vfm_sink, &wc_data->sink_case);
+ }
+ else
+ vfm_sink->class->write (vfm_sink, &wc_data->trns_case);
+ }
+
+ /* Execute temporary transformations. */
+ if (temporary_trns_chain != NULL)
+ {
+ retval = trns_chain_execute (temporary_trns_chain,
+ &wc_data->trns_case,
+ &wc_data->cases_written);
+ if (retval != TRNS_CONTINUE)
+ goto done;
+ }
+
+ /* Pass case to procedure. */
+ wc_data->cases_analyzed++;
+ if (wc_data->proc_func != NULL)
+ if (!wc_data->proc_func (&wc_data->trns_case, wc_data->aux))
+ retval = TRNS_ERROR;
+
+ done:
+ clear_case (&wc_data->trns_case);
+ return retval != TRNS_ERROR;
+}
+
+/* Add C to the lag queue. */
+static void
+lag_case (const struct ccase *c)
+{
+ if (lag_count < n_lag)
+ lag_count++;
+ case_destroy (&lag_queue[lag_head]);
+ case_clone (&lag_queue[lag_head], c);
+ if (++lag_head >= n_lag)
+ lag_head = 0;
+}
+
+/* Clears the variables in C that need to be cleared between
+ processing cases. */
+static void
+clear_case (struct ccase *c)
+{
+ size_t var_cnt = dict_get_var_cnt (default_dict);
+ size_t i;
+
+ for (i = 0; i < var_cnt; i++)
+ {
+ struct variable *v = dict_get_var (default_dict, i);
+ if (!v->leave)
+ {
+ if (v->type == NUMERIC)
+ case_data_rw (c, v->fv)->f = SYSMIS;
+ else
+ memset (case_data_rw (c, v->fv)->s, ' ', v->width);
+ }
+ }
+}
+
+/* Closes the active file. */
+static bool
+close_active_file (void)
+{
+ /* Free memory for lag queue, and turn off lagging. */
+ if (n_lag > 0)
+ {
+ int i;
+
+ for (i = 0; i < n_lag; i++)
+ case_destroy (&lag_queue[i]);
+ free (lag_queue);
+ n_lag = 0;
+ }
+
+ /* Dictionary from before TEMPORARY becomes permanent. */
+ proc_cancel_temporary_transformations ();
+
+ /* Finish compaction. */
+ if (compactor != NULL)
+ {
+ dict_compactor_destroy (compactor);
+ dict_compact_values (default_dict);
+ }
+
+ /* Free data source. */
+ free_case_source (vfm_source);
+ vfm_source = NULL;
+
+ /* Old data sink becomes new data source. */
+ if (vfm_sink->class->make_source != NULL)
+ vfm_source = vfm_sink->class->make_source (vfm_sink);
+ free_case_sink (vfm_sink);
+ vfm_sink = NULL;
+
+ /* Cancel TEMPORARY, PROCESS IF, FILTER, N OF CASES, vectors,
+ and get rid of all the transformations. */
+ dict_clear_vectors (default_dict);
+ permanent_dict = NULL;
+ return proc_cancel_all_transformations ();
+}
+\f
+/* Returns a pointer to the lagged case from N_BEFORE cases before the
+ current one, or NULL if there haven't been that many cases yet. */
+struct ccase *
+lagged_case (int n_before)
+{
+ assert (n_before >= 1 );
+ assert (n_before <= n_lag);
+
+ if (n_before <= lag_count)
+ {
+ int index = lag_head - n_before;
+ if (index < 0)
+ index += n_lag;
+ return &lag_queue[index];
+ }
+ else
+ return NULL;
+}
+\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. */
+ bool (*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 bool 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.
+
+ Returns true if successful, false if an I/O error occurred. */
+bool
+procedure_with_splits (void (*begin_func) (void *aux),
+ bool (*proc_func) (struct ccase *, void *aux),
+ void (*end_func) (void *aux),
+ void *func_aux)
+{
+ struct split_aux_data split_aux;
+ bool ok;
+
+ 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 ();
+ ok = internal_procedure (procedure_with_splits_callback, &split_aux);
+ if (split_aux.case_count > 0 && end_func != NULL)
+ end_func (func_aux);
+ if (!close_active_file ())
+ ok = false;
+
+ case_destroy (&split_aux.prev_case);
+
+ return ok;
+}
+
+/* procedure() callback used by procedure_with_splits(). */
+static bool
+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))
+ {
+ if (split_aux->case_count > 0 && split_aux->end_func != NULL)
+ split_aux->end_func (split_aux->func_aux);
+
+ dump_splits (c);
+ case_destroy (&split_aux->prev_case);
+ case_clone (&split_aux->prev_case, c);
+
+ if (split_aux->begin_func != NULL)
+ split_aux->begin_func (split_aux->func_aux);
+ }
+
+ split_aux->case_count++;
+ if (split_aux->proc_func != NULL)
+ return split_aux->proc_func (c, split_aux->func_aux);
+ else
+ return true;
+}
+
+/* Compares the SPLIT FILE variables in cases A and B and returns
+ nonzero only if they differ. */
+static int
+equal_splits (const struct ccase *a, const struct ccase *b)
+{
+ return case_compare (a, b,
+ dict_get_split_vars (default_dict),
+ dict_get_split_cnt (default_dict)) == 0;
+}
+
+/* Dumps out the values of all the split variables for the case C. */
+static void
+dump_splits (struct ccase *c)
+{
+ struct variable *const *split;
+ struct tab_table *t;
+ size_t split_cnt;
+ int i;
+
+ split_cnt = dict_get_split_cnt (default_dict);
+ if (split_cnt == 0)
+ return;
+
+ t = tab_create (3, split_cnt + 1, 0);
+ tab_dim (t, tab_natural_dimensions);
+ tab_vline (t, TAL_GAP, 1, 0, split_cnt);
+ tab_vline (t, TAL_GAP, 2, 0, split_cnt);
+ tab_text (t, 0, 0, TAB_NONE, _("Variable"));
+ tab_text (t, 1, 0, TAB_LEFT, _("Value"));
+ tab_text (t, 2, 0, TAB_LEFT, _("Label"));
+ split = dict_get_split_vars (default_dict);
+ for (i = 0; i < split_cnt; i++)
+ {
+ struct variable *v = split[i];
+ char temp_buf[80];
+ const char *val_lab;
+
+ assert (v->type == NUMERIC || v->type == ALPHA);
+ tab_text (t, 0, i + 1, TAB_LEFT | TAT_PRINTF, "%s", v->name);
+
+ data_out (temp_buf, &v->print, case_data (c, v->fv));
+
+ temp_buf[v->print.w] = 0;
+ tab_text (t, 1, i + 1, TAT_PRINTF, "%.*s", v->print.w, temp_buf);
+
+ val_lab = val_labs_find (v->val_labs, *case_data (c, v->fv));
+ if (val_lab)
+ tab_text (t, 2, i + 1, TAB_LEFT, val_lab);
+ }
+ tab_flags (t, SOMF_NO_TITLE);
+ tab_submit (t);
+}
+\f
+/* Represents auxiliary data for handling SPLIT FILE in a
+ multipass procedure. */
+struct multipass_split_aux_data
+ {
+ struct ccase prev_case; /* Data in previous case. */
+ struct casefile *casefile; /* Accumulates data for a split. */
+
+ /* Function to call with the accumulated data. */
+ bool (*split_func) (const struct casefile *, void *);
+ void *func_aux; /* Auxiliary data. */
+ };
+
+static bool multipass_split_callback (struct ccase *c, void *aux_);
+static bool multipass_split_output (struct multipass_split_aux_data *);
+
+/* Returns true if successful, false if an I/O error occurred. */
+bool
+multipass_procedure_with_splits (bool (*split_func) (const struct casefile *,
+ void *),
+ void *func_aux)
+{
+ struct multipass_split_aux_data aux;
+ bool ok;
+
+ assert (split_func != NULL);
+
+ open_active_file ();
+
+ case_nullify (&aux.prev_case);
+ aux.casefile = NULL;
+ aux.split_func = split_func;
+ aux.func_aux = func_aux;
+
+ ok = internal_procedure (multipass_split_callback, &aux);
+ if (aux.casefile != NULL)
+ ok = multipass_split_output (&aux) && ok;
+ case_destroy (&aux.prev_case);
+
+ if (!close_active_file ())
+ ok = false;
+
+ return ok;
+}
+
+/* procedure() callback used by multipass_procedure_with_splits(). */
+static bool
+multipass_split_callback (struct ccase *c, void *aux_)
+{
+ struct multipass_split_aux_data *aux = aux_;
+ bool ok = true;
+
+ /* Start a new series if needed. */
+ if (aux->casefile == NULL || !equal_splits (c, &aux->prev_case))
+ {
+ /* Pass any cases to split_func. */
+ if (aux->casefile != NULL)
+ ok = multipass_split_output (aux);
+
+ /* Start a new casefile. */
+ aux->casefile = casefile_create (dict_get_next_value_idx (default_dict));
+
+ /* Record split values. */
+ dump_splits (c);
+ case_destroy (&aux->prev_case);
+ case_clone (&aux->prev_case, c);
+ }
+
+ return casefile_append (aux->casefile, c) && ok;
+}
+
+static bool
+multipass_split_output (struct multipass_split_aux_data *aux)
+{
+ bool ok;
+
+ assert (aux->casefile != NULL);
+ ok = aux->split_func (aux->casefile, aux->func_aux);
+ casefile_destroy (aux->casefile);
+ aux->casefile = NULL;
+
+ return ok;
+}
+
+/* Discards all the current state in preparation for a data-input
+ command like DATA LIST or GET. */
+void
+discard_variables (void)
+{
+ dict_clear (default_dict);
+ fh_set_default_handle (NULL);
+
+ n_lag = 0;
+
+ if (vfm_source != NULL)
+ {
+ free_case_source (vfm_source);
+ vfm_source = NULL;
+ }
+
+ proc_cancel_all_transformations ();
+
+ expr_free (process_if_expr);
+ process_if_expr = NULL;
+
+ 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;
+}
--- /dev/null
+/* PSPP - computes sample statistics.
+ Copyright (C) 1997-9, 2000, 2006 Free Software Foundation, Inc.
+ Written by Ben Pfaff <blp@gnu.org>.
+
+ 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 the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#ifndef PROCEDURE_H
+#define PROCEDURE_H 1
+
+#include <time.h>
+#include <stdbool.h>
+#include <data/transformations.h>
+
+struct ccase;
+struct casefile;
+struct case_sink;
+struct case_source;
+
+/* Dictionary produced by permanent and temporary transformations
+ on data from the source. */
+extern struct dictionary *default_dict;
+\f
+/* Transformations. */
+
+void add_transformation (trns_proc_func *, trns_free_func *, void *);
+void add_transformation_with_finalizer (trns_finalize_func *,
+ trns_proc_func *,
+ trns_free_func *, void *);
+size_t next_transformation (void);
+
+void discard_variables (void);
+
+bool proc_cancel_all_transformations (void);
+struct trns_chain *proc_capture_transformations (void);
+
+void proc_start_temporary_transformations (void);
+bool proc_in_temporary_transformations (void);
+bool proc_make_temporary_transformations_permanent (void);
+bool proc_cancel_temporary_transformations (void);
+\f
+/* Procedures. */
+
+void proc_init (void);
+void proc_done (void);
+
+void proc_set_source (struct case_source *);
+bool proc_has_source (void);
+
+void proc_set_sink (struct case_sink *);
+struct casefile *proc_capture_output (void);
+
+bool procedure (bool (*proc_func) (struct ccase *, void *aux), void *aux);
+bool procedure_with_splits (void (*begin_func) (void *aux),
+ bool (*proc_func) (struct ccase *, void *aux),
+ void (*end_func) (void *aux),
+ void *aux);
+bool multipass_procedure (bool (*proc_func) (const struct casefile *,
+ void *aux),
+ void *aux);
+bool multipass_procedure_with_splits (bool (*) (const struct casefile *,
+ void *),
+ void *aux);
+time_t time_of_last_procedure (void);
+\f
+/* Number of cases to lag. */
+extern int n_lag;
+
+struct ccase *lagged_case (int n_before);
+
+#endif /* procedure.h */
#include <stdlib.h>
#include <libpspp/str.h>
-#include <procedure.h>
#include "xalloc.h"
#include <unistd.h>
#include <data/dictionary.h>
+#include <data/procedure.h>
#include <data/settings.h>
#include <data/variable.h>
#include <language/lexer/lexer.h>
#include <libpspp/str.h>
#include <output/manager.h>
#include <output/table.h>
-#include <procedure.h>
#if HAVE_SYS_WAIT_H
#include <sys/wait.h>
#include <stdlib.h>
#include "control-stack.h"
-#include <procedure.h>
+#include <data/procedure.h>
#include <data/transformations.h>
#include <data/variable.h>
#include <language/command.h>
#include "control-stack.h"
#include <data/case.h>
#include <data/dictionary.h>
-#include <procedure.h>
+#include <data/procedure.h>
#include <data/settings.h>
#include <data/transformations.h>
#include <data/variable.h>
#include <stdlib.h>
#include <data/dictionary.h>
-#include <procedure.h>
+#include <data/procedure.h>
#include <data/settings.h>
#include <language/command.h>
#include <language/lexer/lexer.h>
#include "control-stack.h"
#include <data/dictionary.h>
-#include <procedure.h>
+#include <data/procedure.h>
#include <data/transformations.h>
#include <data/value-labels.h>
#include <data/variable.h>
#include <data/data-in.h>
#include <data/dictionary.h>
#include <data/format.h>
+#include <data/procedure.h>
#include <data/settings.h>
#include <data/transformations.h>
#include <data/variable.h>
#include <libpspp/misc.h>
#include <libpspp/str.h>
#include <output/table.h>
-#include <procedure.h>
#include "gettext.h"
#define _(msgid) gettext (msgid)
02110-1301, USA. */
#include <config.h>
+
#include <language/data-io/data-reader.h>
+
#include <ctype.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
-#include <libpspp/alloc.h>
-#include <language/command.h>
-#include <libpspp/message.h>
-#include <language/data-io/file-handle.h>
+
#include <data/file-handle-def.h>
#include <data/file-name.h>
-#include <language/line-buffer.h>
+#include <data/procedure.h>
+#include <language/command.h>
+#include <language/data-io/file-handle.h>
#include <language/lexer/lexer.h>
+#include <language/line-buffer.h>
+#include <libpspp/alloc.h>
+#include <libpspp/message.h>
#include <libpspp/str.h>
-#include <procedure.h>
#include "gettext.h"
#define _(msgid) gettext (msgid)
#include <data/case.h>
#include <data/dictionary.h>
#include <data/por-file-writer.h>
+#include <data/procedure.h>
#include <data/settings.h>
#include <data/storage-stream.h>
#include <data/sys-file-writer.h>
#include <libpspp/message.h>
#include <libpspp/misc.h>
#include <libpspp/str.h>
-#include <procedure.h>
#include "gettext.h"
#define _(msgid) gettext (msgid)
#include <data/case.h>
#include <data/case-source.h>
#include <data/dictionary.h>
+#include <data/procedure.h>
#include <data/transformations.h>
#include <data/variable.h>
#include <language/command.h>
#include <libpspp/message.h>
#include <libpspp/misc.h>
#include <libpspp/str.h>
-#include <procedure.h>
#include "gettext.h"
#define _(msgid) gettext (msgid)
02110-1301, USA. */
#include <config.h>
-#include <libpspp/message.h>
+
#include <stdio.h>
#include <stdlib.h>
-#include <libpspp/alloc.h>
+
+#include "intprops.h"
+#include "size_max.h"
#include <data/case.h>
-#include <language/command.h>
-#include <libpspp/compiler.h>
#include <data/dictionary.h>
-#include "intprops.h"
+#include <data/format.h>
+#include <data/procedure.h>
+#include <data/variable.h>
+#include <language/command.h>
#include <language/lexer/lexer.h>
-#include <libpspp/message.h>
+#include <libpspp/alloc.h>
+#include <libpspp/compiler.h>
#include <libpspp/magic.h>
+#include <libpspp/message.h>
+#include <libpspp/message.h>
#include <libpspp/misc.h>
#include <output/htmlP.h>
-#include <output/output.h>
-#include "size_max.h"
#include <output/manager.h>
+#include <output/output.h>
#include <output/table.h>
-#include <data/variable.h>
-#include <procedure.h>
-#include <data/format.h>
#include "gettext.h"
#define _(msgid) gettext (msgid)
#include <data/case.h>
#include <data/data-in.h>
#include <data/dictionary.h>
+#include <data/procedure.h>
#include <data/variable.h>
#include <language/command.h>
#include <language/data-io/data-reader.h>
#include <libpspp/misc.h>
#include <libpspp/pool.h>
#include <libpspp/str.h>
-#include <procedure.h>
#include "gettext.h"
#define _(msgid) gettext (msgid)
#include <stdlib.h>
#include <data/case.h>
-#include <procedure.h>
+#include <data/procedure.h>
#include <data/transformations.h>
#include <data/variable.h>
#include <language/command.h>
#include <data/any-reader.h>
#include <data/dictionary.h>
#include <data/file-handle-def.h>
-#include <procedure.h>
+#include <data/procedure.h>
#include <data/value-labels.h>
#include <data/variable.h>
#include <language/command.h>
#include <stdio.h>
#include <stdlib.h>
-#include <procedure.h>
+#include <data/procedure.h>
#include <data/variable.h>
#include <language/command.h>
#include <language/lexer/lexer.h>
#include <stdlib.h>
#include <data/data-in.h>
-#include <procedure.h>
+#include <data/procedure.h>
#include <data/variable.h>
#include <language/command.h>
#include <language/lexer/lexer.h>
#include <language/lexer/lexer.h>
#include <libpspp/misc.h>
#include <libpspp/str.h>
+#include <data/procedure.h>
#include <data/variable.h>
-#include <procedure.h>
#include "gettext.h"
#define _(msgid) gettext (msgid)
#include <stdlib.h>
#include <data/dictionary.h>
-#include <procedure.h>
+#include <data/procedure.h>
#include <data/variable.h>
#include <language/command.h>
#include <language/lexer/lexer.h>
#include <stdlib.h>
#include <data/dictionary.h>
-#include <procedure.h>
+#include <data/procedure.h>
#include <data/variable.h>
#include <language/command.h>
#include <language/lexer/lexer.h>
#include <stdlib.h>
#include <data/dictionary.h>
-#include <procedure.h>
+#include <data/procedure.h>
#include <data/variable.h>
#include <language/command.h>
#include <language/lexer/lexer.h>
#include <data/dictionary.h>
#include <data/file-handle-def.h>
-#include <procedure.h>
+#include <data/procedure.h>
#include <data/sys-file-reader.h>
#include <data/value-labels.h>
#include <data/variable.h>
#include <stdio.h>
#include <stdlib.h>
-#include <procedure.h>
+#include <data/procedure.h>
#include <data/value-labels.h>
#include <data/variable.h>
#include <language/command.h>
#include <stdio.h>
#include <stdlib.h>
-#include <procedure.h>
+#include <data/procedure.h>
#include <data/variable.h>
#include <language/command.h>
#include <language/lexer/lexer.h>
#include <stdio.h>
#include <stdlib.h>
-#include <procedure.h>
+#include <data/procedure.h>
#include <data/variable.h>
#include <language/command.h>
#include <language/lexer/lexer.h>
#include <stdlib.h>
-#include <procedure.h>
+#include <data/procedure.h>
#include <data/dictionary.h>
#include <data/variable.h>
#include <language/command.h>
#include <stdio.h>
-#include <procedure.h>
+#include <data/procedure.h>
#include <data/dictionary.h>
#include <data/variable.h>
#include <language/command.h>
#include <limits.h>
#include <math.h>
#include <stdbool.h>
+
+#include <data/calendar.h>
#include <data/case.h>
-#include <libpspp/compiler.h>
#include <data/data-in.h>
#include <data/dictionary.h>
-#include <libpspp/message.h>
-#include <data/calendar.h>
+#include <data/procedure.h>
+#include <data/settings.h>
+#include <data/value.h>
+#include <data/variable.h>
#include <gsl-extras/gsl-extras.h>
+#include <libpspp/compiler.h>
+#include <libpspp/message.h>
#include <libpspp/misc.h>
+#include <libpspp/str.h>
#include <math/moments.h>
#include <math/random.h>
-#include <data/settings.h>
-#include <libpspp/str.h>
-#include <data/value.h>
-#include <data/variable.h>
-#include <procedure.h>
#include "gettext.h"
#define _(msgid) gettext (msgid)
#include <data/settings.h>
#include <libpspp/str.h>
#include <data/variable.h>
-#include <procedure.h>
\f
/* Declarations. */
#include "lexer.h"
#include <data/dictionary.h>
-#include <procedure.h>
+#include <data/procedure.h>
#include <data/variable.h>
#include <libpspp/alloc.h>
#include <libpspp/bit-vector.h>
#include <data/casefile.h>
#include <data/dictionary.h>
#include <data/file-handle-def.h>
-#include <procedure.h>
+#include <data/procedure.h>
#include <data/settings.h>
#include <data/storage-stream.h>
#include <data/sys-file-writer.h>
#include <libpspp/str.h>
#include <math/moments.h>
#include <math/sort.h>
-#include <procedure.h>
#include "gettext.h"
#define _(msgid) gettext (msgid)
#include <data/case.h>
#include <data/dictionary.h>
+#include <data/procedure.h>
#include <data/transformations.h>
#include <data/variable.h>
#include <language/command.h>
#include <libpspp/message.h>
#include <libpspp/pool.h>
#include <libpspp/str.h>
-#include <procedure.h>
#include "gettext.h"
#define _(msgid) gettext (msgid)
#include <data/dictionary.h>
#include <data/file-handle-def.h>
-#include <procedure.h>
+#include <data/procedure.h>
#include <data/variable.h>
#include <language/command.h>
#include <language/data-io/file-handle.h>
*/
#include <config.h>
-#include <libpspp/message.h>
+
#include <ctype.h>
+#include <gsl/gsl_cdf.h>
#include <stdlib.h>
#include <stdio.h>
-#include <gsl/gsl_cdf.h>
-#include <libpspp/array.h>
-#include <libpspp/alloc.h>
+
#include <data/case.h>
#include <data/dictionary.h>
-#include <libpspp/hash.h>
-#include <libpspp/pool.h>
+#include <data/procedure.h>
+#include <data/value-labels.h>
+#include <data/variable.h>
#include <language/command.h>
-#include <libpspp/compiler.h>
#include <language/lexer/lexer.h>
-#include <libpspp/message.h>
+#include <libpspp/alloc.h>
+#include <libpspp/array.h>
+#include <libpspp/compiler.h>
+#include <libpspp/hash.h>
#include <libpspp/magic.h>
+#include <libpspp/message.h>
+#include <libpspp/message.h>
#include <libpspp/misc.h>
-#include <output/output.h>
+#include <libpspp/pool.h>
#include <libpspp/str.h>
+#include <output/output.h>
#include <output/table.h>
-#include <data/value-labels.h>
-#include <data/variable.h>
-#include <procedure.h>
#include "gettext.h"
#define _(msgid) gettext (msgid)
#include <data/case.h>
#include <data/casefile.h>
#include <data/dictionary.h>
+#include <data/procedure.h>
#include <data/transformations.h>
#include <data/variable.h>
#include <language/command.h>
#include <math/moments.h>
#include <output/manager.h>
#include <output/table.h>
-#include <procedure.h>
#include "gettext.h"
#define _(msgid) gettext (msgid)
02110-1301, USA. */
#include <config.h>
+
#include <gsl/gsl_cdf.h>
#include <libpspp/message.h>
+#include <math.h>
#include <stdio.h>
#include <stdlib.h>
-#include <math.h>
-#include <libpspp/alloc.h>
-#include <libpspp/str.h>
+
#include <data/case.h>
+#include <data/casefile.h>
#include <data/dictionary.h>
+#include <data/procedure.h>
+#include <data/value-labels.h>
+#include <data/variable.h>
#include <language/command.h>
-#include <libpspp/compiler.h>
#include <language/lexer/lexer.h>
-#include <libpspp/message.h>
+#include <libpspp/alloc.h>
+#include <libpspp/compiler.h>
+#include <libpspp/hash.h>
#include <libpspp/magic.h>
+#include <libpspp/message.h>
#include <libpspp/misc.h>
-#include <output/table.h>
-#include <output/manager.h>
-#include <data/value-labels.h>
-#include <data/variable.h>
-#include <procedure.h>
-#include <libpspp/hash.h>
-#include <data/casefile.h>
+#include <libpspp/str.h>
#include <math/factor-stats.h>
#include <math/moments.h>
#include <math/percentiles.h>
#include <output/charts/box-whisker.h>
#include <output/charts/cartesian.h>
+#include <output/manager.h>
+#include <output/table.h>
#include "gettext.h"
#define _(msgid) gettext (msgid)
#include <data/case-source.h>
#include <data/case.h>
#include <data/dictionary.h>
-#include <procedure.h>
+#include <data/procedure.h>
#include <data/settings.h>
#include <data/value.h>
#include <data/variable.h>
#include <libpspp/misc.h>
#include <libpspp/pool.h>
#include <libpspp/str.h>
-#include <procedure.h>
#include "intprops.h"
*/
#include <config.h>
-#include <libpspp/message.h>
+
#include <math.h>
#include <stdlib.h>
#include <gsl/gsl_histogram.h>
-#include <libpspp/alloc.h>
-#include <libpspp/bit-vector.h>
#include <data/case.h>
-#include <libpspp/compiler.h>
#include <data/dictionary.h>
-#include <libpspp/hash.h>
-#include <libpspp/pool.h>
+#include <data/procedure.h>
+#include <data/settings.h>
+#include <data/value-labels.h>
+#include <data/variable.h>
#include <language/command.h>
#include <language/lexer/lexer.h>
-#include <math/moments.h>
-#include <libpspp/message.h>
+#include <libpspp/alloc.h>
#include <libpspp/array.h>
+#include <libpspp/bit-vector.h>
+#include <libpspp/compiler.h>
+#include <libpspp/hash.h>
#include <libpspp/magic.h>
+#include <libpspp/message.h>
+#include <libpspp/message.h>
#include <libpspp/misc.h>
-#include <output/output.h>
-#include <output/manager.h>
+#include <libpspp/pool.h>
#include <libpspp/str.h>
-#include <output/table.h>
-#include <data/value-labels.h>
-#include <data/variable.h>
-#include <procedure.h>
-#include <data/settings.h>
-#include <output/charts/piechart.h>
+#include <math/histogram.h>
+#include <math/moments.h>
#include <output/chart.h>
+#include <output/charts/piechart.h>
#include <output/charts/plot-hist.h>
-#include <math/histogram.h>
+#include <output/manager.h>
+#include <output/output.h>
+#include <output/table.h>
#include "gettext.h"
#define _(msgid) gettext (msgid)
#include <stdio.h>
#include <data/dictionary.h>
-#include <procedure.h>
+#include <data/procedure.h>
#include <data/variable.h>
#include <language/command.h>
#include <language/lexer/lexer.h>
02110-1301, USA. */
#include <config.h>
+
#include <gsl/gsl_cdf.h>
-#include <libpspp/message.h>
+#include <math.h>
#include <stdio.h>
#include <stdlib.h>
-#include <math.h>
-#include <libpspp/alloc.h>
-#include <libpspp/str.h>
+
#include <data/case.h>
+#include <data/casefile.h>
#include <data/dictionary.h>
+#include <data/procedure.h>
+#include <data/value-labels.h>
+#include <data/variable.h>
#include <language/command.h>
-#include <libpspp/compiler.h>
#include <language/lexer/lexer.h>
-#include <libpspp/message.h>
+#include <libpspp/alloc.h>
+#include <libpspp/compiler.h>
+#include <libpspp/hash.h>
#include <libpspp/magic.h>
+#include <libpspp/message.h>
+#include <libpspp/message.h>
#include <libpspp/misc.h>
-#include <output/table.h>
-#include <output/manager.h>
-#include <data/value-labels.h>
-#include <data/variable.h>
-#include <procedure.h>
-#include <libpspp/hash.h>
-#include <data/casefile.h>
+#include <libpspp/str.h>
#include <math/group-proc.h>
#include <math/group.h>
#include <math/levene.h>
-
+#include <output/manager.h>
+#include <output/table.h>
#include "sort-criteria.h"
#include "gettext.h"
#include <config.h>
#include "sort-criteria.h"
+
#include <data/dictionary.h>
-#include <procedure.h>
+#include <data/procedure.h>
#include <data/variable.h>
#include <language/command.h>
#include <language/stats/sort-criteria.h>
#include <data/category.h>
#include <data/dictionary.h>
#include <data/missing-values.h>
+#include <data/procedure.h>
#include <data/transformations.h>
#include <data/value-labels.h>
#include <data/variable.h>
#include <math/linreg/coefficient.h>
#include <math/linreg/linreg.h>
#include <output/table.h>
-#include <procedure.h>
#include "gettext.h"
#include <limits.h>
#include "sort-criteria.h"
-#include <procedure.h>
+#include <data/procedure.h>
#include <data/settings.h>
#include <data/variable.h>
#include <language/command.h>
02110-1301, USA. */
#include <config.h>
+
#include <gsl/gsl_cdf.h>
-#include <libpspp/message.h>
+#include <math.h>
#include <stdio.h>
#include <stdlib.h>
-#include <math.h>
-#include <libpspp/alloc.h>
+
#include <data/case.h>
#include <data/casefile.h>
+#include <data/dictionary.h>
+#include <data/procedure.h>
+#include <data/value-labels.h>
+#include <data/variable.h>
#include <language/command.h>
+#include <language/lexer/lexer.h>
+#include <libpspp/alloc.h>
#include <libpspp/compiler.h>
-#include <data/dictionary.h>
-#include <libpspp/message.h>
-#include <math/group-proc.h>
#include <libpspp/hash.h>
-#include <math/levene.h>
-#include <language/lexer/lexer.h>
#include <libpspp/magic.h>
+#include <libpspp/message.h>
+#include <libpspp/message.h>
#include <libpspp/misc.h>
-#include "size_max.h"
-#include <output/manager.h>
#include <libpspp/str.h>
+#include <math/group-proc.h>
+#include <math/levene.h>
+#include <output/manager.h>
#include <output/table.h>
-#include <data/value-labels.h>
-#include <data/variable.h>
-#include <procedure.h>
+
+#include "size_max.h"
#include "gettext.h"
#define _(msgid) gettext (msgid)
#include <data/dictionary.h>
#include <data/format.h>
-#include <procedure.h>
+#include <data/procedure.h>
#include <data/settings.h>
#include <data/variable.h>
#include <language/command.h>
02110-1301, USA. */
#include <config.h>
+
#include <ctype.h>
#include <stdlib.h>
-#include <libpspp/alloc.h>
-#include <language/command.h>
+
#include <data/dictionary.h>
-#include <libpspp/message.h>
+#include <data/procedure.h>
+#include <data/variable.h>
+#include <language/command.h>
#include <language/lexer/lexer.h>
-#include <output/output.h>
+#include <libpspp/alloc.h>
+#include <libpspp/message.h>
#include <libpspp/start-date.h>
-#include <data/variable.h>
#include <libpspp/version.h>
-#include <procedure.h>
+#include <output/output.h>
#include "gettext.h"
#define _(msgid) gettext (msgid)
#include <data/case.h>
#include <data/dictionary.h>
-#include <procedure.h>
+#include <data/procedure.h>
#include <data/transformations.h>
#include <data/variable.h>
#include <language/command.h>
#include <data/case.h>
#include <data/dictionary.h>
-#include <procedure.h>
+#include <data/procedure.h>
#include <data/transformations.h>
#include <data/variable.h>
#include <language/command.h>
#include <data/case.h>
#include <data/data-in.h>
#include <data/dictionary.h>
-#include <procedure.h>
+#include <data/procedure.h>
#include <data/transformations.h>
#include <data/variable.h>
#include <language/command.h>
#include <stdio.h>
#include <math.h>
+#include <data/procedure.h>
#include <data/variable.h>
#include <language/command.h>
#include <language/lexer/lexer.h>
#include <libpspp/message.h>
#include <libpspp/str.h>
#include <math/random.h>
-#include <procedure.h>
#include "gettext.h"
#define _(msgid) gettext (msgid)
#include <data/dictionary.h>
#include <data/transformations.h>
-#include <procedure.h>
+#include <data/procedure.h>
#include <data/variable.h>
#include <language/command.h>
#include <language/expressions/public.h>
#include <libpspp/hash.h>
#include <libpspp/str.h>
#include <data/variable.h>
-#include <procedure.h>
+#include <data/procedure.h>
#include <libpspp/alloc.h>
#include <libpspp/misc.h>
#include "group.h"
#include <data/case-source.h>
#include <data/case.h>
#include <data/casefile.h>
+#include <data/procedure.h>
#include <data/settings.h>
#include <data/variable.h>
#include <data/storage-stream.h>
#include <libpspp/message.h>
#include <libpspp/misc.h>
#include <libpspp/str.h>
-#include <procedure.h>
#include "gettext.h"
#define _(msgid) gettext (msgid)
+++ /dev/null
-/* PSPP - computes sample statistics.
- Copyright (C) 1997-9, 2000, 2006 Free Software Foundation, Inc.
- Written by Ben Pfaff <blp@gnu.org>.
-
- 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 the Free Software Foundation; either version 2 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- 02110-1301, USA. */
-
-#include <config.h>
-
-#include <procedure.h>
-
-#include <errno.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-
-#include "expressions/public.h"
-#include <data/case-source.h>
-#include <data/case-sink.h>
-#include <data/case.h>
-#include <data/casefile.h>
-#include <data/dictionary.h>
-#include <data/file-handle-def.h>
-#include <data/settings.h>
-#include <data/storage-stream.h>
-#include <data/transformations.h>
-#include <data/value-labels.h>
-#include <data/variable.h>
-#include <libpspp/alloc.h>
-#include <libpspp/message.h>
-#include <libpspp/misc.h>
-#include <libpspp/str.h>
-#include <output/manager.h>
-#include <output/table.h>
-
-#include "gettext.h"
-#define _(msgid) gettext (msgid)
-
-/*
- Virtual File Manager (vfm):
-
- vfm is used to process data files. It uses the model that
- data is read from one stream (the data source), processed,
- then written to another (the data sink). The data source is
- then deleted and the data sink becomes the data source for the
- next procedure. */
-
-/* Procedure execution data. */
-struct write_case_data
- {
- /* Function to call for each case. */
- bool (*proc_func) (struct ccase *, void *); /* Function. */
- void *aux; /* Auxiliary data. */
-
- struct ccase trns_case; /* Case used for transformations. */
- struct ccase sink_case; /* Case written to sink, if
- compaction is necessary. */
- size_t cases_written; /* Cases output so far. */
- size_t cases_analyzed; /* Cases passed to procedure so far. */
- };
-
-/* Cases are read from vfm_source,
- pass through permanent_trns_chain (which transforms them into
- the format described by permanent_dict),
- are written to vfm_sink,
- pass through temporary_trns_chain (which transforms them into
- the format described by default_dict),
- and are finally passed to the procedure. */
-static struct case_source *vfm_source;
-static struct trns_chain *permanent_trns_chain;
-static struct dictionary *permanent_dict;
-static struct case_sink *vfm_sink;
-static struct trns_chain *temporary_trns_chain;
-struct dictionary *default_dict;
-
-/* The transformation chain that the next transformation will be
- added to. */
-static struct trns_chain *cur_trns_chain;
-
-/* The compactor used to compact a case, if necessary;
- otherwise a null pointer. */
-static struct dict_compactor *compactor;
-
-/* Time at which vfm was last invoked. */
-static time_t last_vfm_invocation;
-
-/* Lag queue. */
-int n_lag; /* Number of cases to lag. */
-static int lag_count; /* Number of cases in lag_queue so far. */
-static int lag_head; /* Index where next case will be added. */
-static struct ccase *lag_queue; /* Array of n_lag ccase * elements. */
-
-static void add_case_limit_trns (void);
-static void add_filter_trns (void);
-static void add_process_if_trns (void);
-
-static bool internal_procedure (bool (*proc_func) (struct ccase *, void *),
- void *aux);
-static void update_last_vfm_invocation (void);
-static void create_trns_case (struct ccase *, struct dictionary *);
-static void open_active_file (void);
-static bool write_case (struct write_case_data *wc_data);
-static void lag_case (const struct ccase *c);
-static void clear_case (struct ccase *c);
-static bool close_active_file (void);
-\f
-/* Public functions. */
-
-/* Returns the last time the data was read. */
-time_t
-time_of_last_procedure (void)
-{
- if (last_vfm_invocation == 0)
- update_last_vfm_invocation ();
- return last_vfm_invocation;
-}
-
-/* Reads the data from the input program and writes it to a new
- active file. For each case we read from the input program, we
- do the following:
-
- 1. Execute permanent transformations. If these drop the case,
- start the next case from step 1.
-
- 2. Write case to replacement active file.
-
- 3. Execute temporary transformations. If these drop the case,
- start the next case from step 1.
-
- 4. Pass case to PROC_FUNC, passing AUX as auxiliary data.
-
- Returns true if successful, false if an I/O error occurred. */
-bool
-procedure (bool (*proc_func) (struct ccase *, void *), void *aux)
-{
- if (proc_func == NULL
- && case_source_is_class (vfm_source, &storage_source_class)
- && vfm_sink == NULL
- && temporary_trns_chain == NULL
- && trns_chain_is_empty (permanent_trns_chain))
- {
- expr_free (process_if_expr);
- process_if_expr = NULL;
- dict_set_case_limit (default_dict, 0);
- dict_clear_vectors (default_dict);
-
- update_last_vfm_invocation ();
- return true;
- }
- else
- {
- bool ok;
-
- open_active_file ();
- ok = internal_procedure (proc_func, aux);
- ok = close_active_file () && ok;
-
- return ok;
- }
-}
-
-/* Callback function for multipass_procedure(). */
-static bool
-multipass_callback (struct ccase *c, void *cf_)
-{
- struct casefile *cf = cf_;
- return casefile_append (cf, c);
-}
-
-/* Procedure that allows multiple passes over the input data.
- The entire active file is passed to PROC_FUNC, with the given
- AUX as auxiliary data, as a unit. */
-bool
-multipass_procedure (bool (*proc_func) (const struct casefile *, void *aux),
- void *aux)
-{
- if (case_source_is_class (vfm_source, &storage_source_class)
- && vfm_sink == NULL
- && temporary_trns_chain == NULL
- && trns_chain_is_empty (permanent_trns_chain))
- {
- proc_func (storage_source_get_casefile (vfm_source), aux);
-
- expr_free (process_if_expr);
- process_if_expr = NULL;
- dict_set_case_limit (default_dict, 0);
- dict_clear_vectors (default_dict);
-
- update_last_vfm_invocation ();
- return true;
- }
- else
- {
- struct casefile *cf;
- bool ok;
-
- assert (proc_func != NULL);
-
- cf = casefile_create (dict_get_next_value_idx (default_dict));
-
- open_active_file ();
- ok = internal_procedure (multipass_callback, cf);
- ok = proc_func (cf, aux) && ok;
- ok = close_active_file () && ok;
-
- casefile_destroy (cf);
-
- return ok;
- }
-}
-
-/* Executes a procedure, as procedure(), except that the caller
- is responsible for calling open_active_file() and
- close_active_file().
- Returns true if successful, false if an I/O error occurred. */
-static bool
-internal_procedure (bool (*proc_func) (struct ccase *, void *), void *aux)
-{
- struct write_case_data wc_data;
- bool ok;
-
- wc_data.proc_func = proc_func;
- wc_data.aux = aux;
- create_trns_case (&wc_data.trns_case, default_dict);
- case_create (&wc_data.sink_case, dict_get_next_value_idx (default_dict));
- wc_data.cases_written = 0;
-
- update_last_vfm_invocation ();
-
- ok = (vfm_source == NULL
- || vfm_source->class->read (vfm_source,
- &wc_data.trns_case,
- write_case, &wc_data));
-
- case_destroy (&wc_data.sink_case);
- case_destroy (&wc_data.trns_case);
-
- return ok;
-}
-
-/* Updates last_vfm_invocation. */
-static void
-update_last_vfm_invocation (void)
-{
- last_vfm_invocation = time (NULL);
-}
-
-/* Creates and returns a case, initializing it from the vectors
- that say which `value's need to be initialized just once, and
- which ones need to be re-initialized before every case. */
-static void
-create_trns_case (struct ccase *trns_case, struct dictionary *dict)
-{
- size_t var_cnt = dict_get_var_cnt (dict);
- size_t i;
-
- case_create (trns_case, dict_get_next_value_idx (dict));
- for (i = 0; i < var_cnt; i++)
- {
- struct variable *v = dict_get_var (dict, i);
- union value *value = case_data_rw (trns_case, v->fv);
-
- if (v->type == NUMERIC)
- value->f = v->leave ? 0.0 : SYSMIS;
- else
- memset (value->s, ' ', v->width);
- }
-}
-
-/* Makes all preparations for reading from the data source and writing
- to the data sink. */
-static void
-open_active_file (void)
-{
- add_case_limit_trns ();
- add_filter_trns ();
- add_process_if_trns ();
-
- /* Finalize transformations. */
- trns_chain_finalize (cur_trns_chain);
-
- /* Make permanent_dict refer to the dictionary right before
- data reaches the sink. */
- if (permanent_dict == NULL)
- permanent_dict = default_dict;
-
- /* Figure out compaction. */
- compactor = (dict_needs_compaction (permanent_dict)
- ? dict_make_compactor (permanent_dict)
- : NULL);
-
- /* Prepare sink. */
- if (vfm_sink == NULL)
- vfm_sink = create_case_sink (&storage_sink_class, permanent_dict, NULL);
- if (vfm_sink->class->open != NULL)
- vfm_sink->class->open (vfm_sink);
-
- /* Allocate memory for lag queue. */
- if (n_lag > 0)
- {
- int i;
-
- lag_count = 0;
- lag_head = 0;
- lag_queue = xnmalloc (n_lag, sizeof *lag_queue);
- for (i = 0; i < n_lag; i++)
- case_nullify (&lag_queue[i]);
- }
-}
-
-/* Transforms trns_case and writes it to the replacement active
- file if advisable. Returns true if more cases can be
- accepted, false otherwise. Do not call this function again
- after it has returned false once. */
-static bool
-write_case (struct write_case_data *wc_data)
-{
- enum trns_result retval;
- size_t case_nr;
-
- /* Execute permanent transformations. */
- case_nr = wc_data->cases_written + 1;
- retval = trns_chain_execute (permanent_trns_chain,
- &wc_data->trns_case, &case_nr);
- if (retval != TRNS_CONTINUE)
- goto done;
-
- /* Write case to LAG queue. */
- if (n_lag)
- lag_case (&wc_data->trns_case);
-
- /* Write case to replacement active file. */
- wc_data->cases_written++;
- if (vfm_sink->class->write != NULL)
- {
- if (compactor != NULL)
- {
- dict_compactor_compact (compactor, &wc_data->sink_case,
- &wc_data->trns_case);
- vfm_sink->class->write (vfm_sink, &wc_data->sink_case);
- }
- else
- vfm_sink->class->write (vfm_sink, &wc_data->trns_case);
- }
-
- /* Execute temporary transformations. */
- if (temporary_trns_chain != NULL)
- {
- retval = trns_chain_execute (temporary_trns_chain,
- &wc_data->trns_case,
- &wc_data->cases_written);
- if (retval != TRNS_CONTINUE)
- goto done;
- }
-
- /* Pass case to procedure. */
- wc_data->cases_analyzed++;
- if (wc_data->proc_func != NULL)
- if (!wc_data->proc_func (&wc_data->trns_case, wc_data->aux))
- retval = TRNS_ERROR;
-
- done:
- clear_case (&wc_data->trns_case);
- return retval != TRNS_ERROR;
-}
-
-/* Add C to the lag queue. */
-static void
-lag_case (const struct ccase *c)
-{
- if (lag_count < n_lag)
- lag_count++;
- case_destroy (&lag_queue[lag_head]);
- case_clone (&lag_queue[lag_head], c);
- if (++lag_head >= n_lag)
- lag_head = 0;
-}
-
-/* Clears the variables in C that need to be cleared between
- processing cases. */
-static void
-clear_case (struct ccase *c)
-{
- size_t var_cnt = dict_get_var_cnt (default_dict);
- size_t i;
-
- for (i = 0; i < var_cnt; i++)
- {
- struct variable *v = dict_get_var (default_dict, i);
- if (!v->leave)
- {
- if (v->type == NUMERIC)
- case_data_rw (c, v->fv)->f = SYSMIS;
- else
- memset (case_data_rw (c, v->fv)->s, ' ', v->width);
- }
- }
-}
-
-/* Closes the active file. */
-static bool
-close_active_file (void)
-{
- /* Free memory for lag queue, and turn off lagging. */
- if (n_lag > 0)
- {
- int i;
-
- for (i = 0; i < n_lag; i++)
- case_destroy (&lag_queue[i]);
- free (lag_queue);
- n_lag = 0;
- }
-
- /* Dictionary from before TEMPORARY becomes permanent. */
- proc_cancel_temporary_transformations ();
-
- /* Finish compaction. */
- if (compactor != NULL)
- {
- dict_compactor_destroy (compactor);
- dict_compact_values (default_dict);
- }
-
- /* Free data source. */
- free_case_source (vfm_source);
- vfm_source = NULL;
-
- /* Old data sink becomes new data source. */
- if (vfm_sink->class->make_source != NULL)
- vfm_source = vfm_sink->class->make_source (vfm_sink);
- free_case_sink (vfm_sink);
- vfm_sink = NULL;
-
- /* Cancel TEMPORARY, PROCESS IF, FILTER, N OF CASES, vectors,
- and get rid of all the transformations. */
- dict_clear_vectors (default_dict);
- permanent_dict = NULL;
- return proc_cancel_all_transformations ();
-}
-\f
-/* Returns a pointer to the lagged case from N_BEFORE cases before the
- current one, or NULL if there haven't been that many cases yet. */
-struct ccase *
-lagged_case (int n_before)
-{
- assert (n_before >= 1 );
- assert (n_before <= n_lag);
-
- if (n_before <= lag_count)
- {
- int index = lag_head - n_before;
- if (index < 0)
- index += n_lag;
- return &lag_queue[index];
- }
- else
- return NULL;
-}
-\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. */
- bool (*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 bool 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.
-
- Returns true if successful, false if an I/O error occurred. */
-bool
-procedure_with_splits (void (*begin_func) (void *aux),
- bool (*proc_func) (struct ccase *, void *aux),
- void (*end_func) (void *aux),
- void *func_aux)
-{
- struct split_aux_data split_aux;
- bool ok;
-
- 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 ();
- ok = internal_procedure (procedure_with_splits_callback, &split_aux);
- if (split_aux.case_count > 0 && end_func != NULL)
- end_func (func_aux);
- if (!close_active_file ())
- ok = false;
-
- case_destroy (&split_aux.prev_case);
-
- return ok;
-}
-
-/* procedure() callback used by procedure_with_splits(). */
-static bool
-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))
- {
- if (split_aux->case_count > 0 && split_aux->end_func != NULL)
- split_aux->end_func (split_aux->func_aux);
-
- dump_splits (c);
- case_destroy (&split_aux->prev_case);
- case_clone (&split_aux->prev_case, c);
-
- if (split_aux->begin_func != NULL)
- split_aux->begin_func (split_aux->func_aux);
- }
-
- split_aux->case_count++;
- if (split_aux->proc_func != NULL)
- return split_aux->proc_func (c, split_aux->func_aux);
- else
- return true;
-}
-
-/* Compares the SPLIT FILE variables in cases A and B and returns
- nonzero only if they differ. */
-static int
-equal_splits (const struct ccase *a, const struct ccase *b)
-{
- return case_compare (a, b,
- dict_get_split_vars (default_dict),
- dict_get_split_cnt (default_dict)) == 0;
-}
-
-/* Dumps out the values of all the split variables for the case C. */
-static void
-dump_splits (struct ccase *c)
-{
- struct variable *const *split;
- struct tab_table *t;
- size_t split_cnt;
- int i;
-
- split_cnt = dict_get_split_cnt (default_dict);
- if (split_cnt == 0)
- return;
-
- t = tab_create (3, split_cnt + 1, 0);
- tab_dim (t, tab_natural_dimensions);
- tab_vline (t, TAL_GAP, 1, 0, split_cnt);
- tab_vline (t, TAL_GAP, 2, 0, split_cnt);
- tab_text (t, 0, 0, TAB_NONE, _("Variable"));
- tab_text (t, 1, 0, TAB_LEFT, _("Value"));
- tab_text (t, 2, 0, TAB_LEFT, _("Label"));
- split = dict_get_split_vars (default_dict);
- for (i = 0; i < split_cnt; i++)
- {
- struct variable *v = split[i];
- char temp_buf[80];
- const char *val_lab;
-
- assert (v->type == NUMERIC || v->type == ALPHA);
- tab_text (t, 0, i + 1, TAB_LEFT | TAT_PRINTF, "%s", v->name);
-
- data_out (temp_buf, &v->print, case_data (c, v->fv));
-
- temp_buf[v->print.w] = 0;
- tab_text (t, 1, i + 1, TAT_PRINTF, "%.*s", v->print.w, temp_buf);
-
- val_lab = val_labs_find (v->val_labs, *case_data (c, v->fv));
- if (val_lab)
- tab_text (t, 2, i + 1, TAB_LEFT, val_lab);
- }
- tab_flags (t, SOMF_NO_TITLE);
- tab_submit (t);
-}
-\f
-/* Represents auxiliary data for handling SPLIT FILE in a
- multipass procedure. */
-struct multipass_split_aux_data
- {
- struct ccase prev_case; /* Data in previous case. */
- struct casefile *casefile; /* Accumulates data for a split. */
-
- /* Function to call with the accumulated data. */
- bool (*split_func) (const struct casefile *, void *);
- void *func_aux; /* Auxiliary data. */
- };
-
-static bool multipass_split_callback (struct ccase *c, void *aux_);
-static bool multipass_split_output (struct multipass_split_aux_data *);
-
-/* Returns true if successful, false if an I/O error occurred. */
-bool
-multipass_procedure_with_splits (bool (*split_func) (const struct casefile *,
- void *),
- void *func_aux)
-{
- struct multipass_split_aux_data aux;
- bool ok;
-
- assert (split_func != NULL);
-
- open_active_file ();
-
- case_nullify (&aux.prev_case);
- aux.casefile = NULL;
- aux.split_func = split_func;
- aux.func_aux = func_aux;
-
- ok = internal_procedure (multipass_split_callback, &aux);
- if (aux.casefile != NULL)
- ok = multipass_split_output (&aux) && ok;
- case_destroy (&aux.prev_case);
-
- if (!close_active_file ())
- ok = false;
-
- return ok;
-}
-
-/* procedure() callback used by multipass_procedure_with_splits(). */
-static bool
-multipass_split_callback (struct ccase *c, void *aux_)
-{
- struct multipass_split_aux_data *aux = aux_;
- bool ok = true;
-
- /* Start a new series if needed. */
- if (aux->casefile == NULL || !equal_splits (c, &aux->prev_case))
- {
- /* Pass any cases to split_func. */
- if (aux->casefile != NULL)
- ok = multipass_split_output (aux);
-
- /* Start a new casefile. */
- aux->casefile = casefile_create (dict_get_next_value_idx (default_dict));
-
- /* Record split values. */
- dump_splits (c);
- case_destroy (&aux->prev_case);
- case_clone (&aux->prev_case, c);
- }
-
- return casefile_append (aux->casefile, c) && ok;
-}
-
-static bool
-multipass_split_output (struct multipass_split_aux_data *aux)
-{
- bool ok;
-
- assert (aux->casefile != NULL);
- ok = aux->split_func (aux->casefile, aux->func_aux);
- casefile_destroy (aux->casefile);
- aux->casefile = NULL;
-
- return ok;
-}
-
-/* Discards all the current state in preparation for a data-input
- command like DATA LIST or GET. */
-void
-discard_variables (void)
-{
- dict_clear (default_dict);
- fh_set_default_handle (NULL);
-
- n_lag = 0;
-
- if (vfm_source != NULL)
- {
- free_case_source (vfm_source);
- vfm_source = NULL;
- }
-
- proc_cancel_all_transformations ();
-
- expr_free (process_if_expr);
- process_if_expr = NULL;
-
- 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;
-}
+++ /dev/null
-/* PSPP - computes sample statistics.
- Copyright (C) 1997-9, 2000, 2006 Free Software Foundation, Inc.
- Written by Ben Pfaff <blp@gnu.org>.
-
- 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 the Free Software Foundation; either version 2 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- 02110-1301, USA. */
-
-#ifndef PROCEDURE_H
-#define PROCEDURE_H 1
-
-#include <time.h>
-#include <stdbool.h>
-#include <data/transformations.h>
-
-struct ccase;
-struct casefile;
-struct case_sink;
-struct case_source;
-
-/* Dictionary produced by permanent and temporary transformations
- on data from the source. */
-extern struct dictionary *default_dict;
-\f
-/* Transformations. */
-
-void add_transformation (trns_proc_func *, trns_free_func *, void *);
-void add_transformation_with_finalizer (trns_finalize_func *,
- trns_proc_func *,
- trns_free_func *, void *);
-size_t next_transformation (void);
-
-void discard_variables (void);
-
-bool proc_cancel_all_transformations (void);
-struct trns_chain *proc_capture_transformations (void);
-
-void proc_start_temporary_transformations (void);
-bool proc_in_temporary_transformations (void);
-bool proc_make_temporary_transformations_permanent (void);
-bool proc_cancel_temporary_transformations (void);
-\f
-/* Procedures. */
-
-void proc_init (void);
-void proc_done (void);
-
-void proc_set_source (struct case_source *);
-bool proc_has_source (void);
-
-void proc_set_sink (struct case_sink *);
-struct casefile *proc_capture_output (void);
-
-bool procedure (bool (*proc_func) (struct ccase *, void *aux), void *aux);
-bool procedure_with_splits (void (*begin_func) (void *aux),
- bool (*proc_func) (struct ccase *, void *aux),
- void (*end_func) (void *aux),
- void *aux);
-bool multipass_procedure (bool (*proc_func) (const struct casefile *,
- void *aux),
- void *aux);
-bool multipass_procedure_with_splits (bool (*) (const struct casefile *,
- void *),
- void *aux);
-time_t time_of_last_procedure (void);
-\f
-/* Number of cases to lag. */
-extern int n_lag;
-
-struct ccase *lagged_case (int n_before);
-
-#endif /* procedure.h */
+Thu May 4 21:50:59 2006 Ben Pfaff <blp@gnu.org>
+
+ Continue reforming procedure execution. In this phase, move
+ procedure.c and procedure.h from src to src/data. Update
+ makefiles and #includes accordingly.
+
+ * automake.mk: (src_ui_terminal_pspp_SOURCES) Remove
+ src/procedure.c and src/procedure.h.
+ (src_ui_terminal_pspp_LDADD) Move libexpressions.a later in list
+ to make the link work.
+
Wed May 3 23:09:37 2006 Ben Pfaff <blp@gnu.org>
Continue reforming procedure execution. In this phase, get rid of
bin_PROGRAMS += src/ui/terminal/pspp
-src_ui_terminal_pspp_SOURCES = \
- src/procedure.c \
- src/procedure.h
+src_ui_terminal_pspp_SOURCES =
src_ui_terminal_pspp_LDADD = \
$(top_builddir)/src/ui/terminal/libui.a \
- $(top_builddir)/src/language/expressions/libexpressions.a \
$(top_builddir)/src/language/liblanguage.a \
$(top_builddir)/src/language/tests/libtests.a \
$(top_builddir)/src/language/utilities/libutilities.a \
$(top_builddir)/src/language/control/libcontrol.a \
+ $(top_builddir)/src/language/expressions/libexpressions.a \
$(top_builddir)/src/language/stats/libstats.a \
$(top_builddir)/src/language/xforms/libxforms.a \
$(top_builddir)/src/language/dictionary/libcmddict.a \
#include "command-line.h"
#include "msg-ui.h"
#include "progname.h"
-#include "procedure.h"
#include "read-line.h"
#include <data/dictionary.h>
#include <data/file-handle-def.h>
#include <data/file-name.h>
+#include <data/procedure.h>
#include <data/settings.h>
#include <data/variable.h>
#include <gsl/gsl_errno.h>
#include <libpspp/version.h>
#include <math/random.h>
#include <output/output.h>
-#include <procedure.h>
#if HAVE_FPU_CONTROL_H
#include <fpu_control.h>