1 /* PSPP - computes sample statistics.
2 Copyright (C) 1997-9, 2000, 2006 Free Software Foundation, Inc.
3 Written by Ben Pfaff <blp@gnu.org>.
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License as
7 published by the Free Software Foundation; either version 2 of the
8 License, or (at your option) any later version.
10 This program is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
22 #include <procedure.h>
29 #include "expressions/public.h"
30 #include <data/case-source.h>
31 #include <data/case-sink.h>
32 #include <data/case.h>
33 #include <data/casefile.h>
34 #include <data/dictionary.h>
35 #include <data/file-handle-def.h>
36 #include <data/settings.h>
37 #include <data/storage-stream.h>
38 #include <data/transformations.h>
39 #include <data/value-labels.h>
40 #include <data/variable.h>
41 #include <libpspp/alloc.h>
42 #include <libpspp/message.h>
43 #include <libpspp/misc.h>
44 #include <libpspp/str.h>
45 #include <output/manager.h>
46 #include <output/table.h>
49 #define _(msgid) gettext (msgid)
52 Virtual File Manager (vfm):
54 vfm is used to process data files. It uses the model that
55 data is read from one stream (the data source), processed,
56 then written to another (the data sink). The data source is
57 then deleted and the data sink becomes the data source for the
60 /* Procedure execution data. */
61 struct write_case_data
63 /* Function to call for each case. */
64 bool (*proc_func) (struct ccase *, void *); /* Function. */
65 void *aux; /* Auxiliary data. */
67 struct ccase trns_case; /* Case used for transformations. */
68 struct ccase sink_case; /* Case written to sink, if
69 compaction is necessary. */
70 size_t cases_written; /* Cases output so far. */
71 size_t cases_analyzed; /* Cases passed to procedure so far. */
74 /* Cases are read from vfm_source,
75 pass through permanent_trns_chain (which transforms them into
76 the format described by permanent_dict),
77 are written to vfm_sink,
78 pass through temporary_trns_chain (which transforms them into
79 the format described by default_dict),
80 and are finally passed to the procedure. */
81 static struct case_source *vfm_source;
82 static struct trns_chain *permanent_trns_chain;
83 static struct dictionary *permanent_dict;
84 static struct case_sink *vfm_sink;
85 static struct trns_chain *temporary_trns_chain;
86 struct dictionary *default_dict;
88 /* The transformation chain that the next transformation will be
90 static struct trns_chain *cur_trns_chain;
92 /* The compactor used to compact a case, if necessary;
93 otherwise a null pointer. */
94 static struct dict_compactor *compactor;
96 /* Time at which vfm was last invoked. */
97 static time_t last_vfm_invocation;
100 int n_lag; /* Number of cases to lag. */
101 static int lag_count; /* Number of cases in lag_queue so far. */
102 static int lag_head; /* Index where next case will be added. */
103 static struct ccase *lag_queue; /* Array of n_lag ccase * elements. */
105 static void add_case_limit_trns (void);
106 static void add_filter_trns (void);
107 static void add_process_if_trns (void);
109 static bool internal_procedure (bool (*proc_func) (struct ccase *, void *),
111 static void update_last_vfm_invocation (void);
112 static void create_trns_case (struct ccase *, struct dictionary *);
113 static void open_active_file (void);
114 static bool write_case (struct write_case_data *wc_data);
115 static void lag_case (const struct ccase *c);
116 static void clear_case (struct ccase *c);
117 static bool close_active_file (void);
119 /* Public functions. */
121 /* Returns the last time the data was read. */
123 time_of_last_procedure (void)
125 if (last_vfm_invocation == 0)
126 update_last_vfm_invocation ();
127 return last_vfm_invocation;
130 /* Reads the data from the input program and writes it to a new
131 active file. For each case we read from the input program, we
134 1. Execute permanent transformations. If these drop the case,
135 start the next case from step 1.
137 2. Write case to replacement active file.
139 3. Execute temporary transformations. If these drop the case,
140 start the next case from step 1.
142 4. Pass case to PROC_FUNC, passing AUX as auxiliary data.
144 Returns true if successful, false if an I/O error occurred. */
146 procedure (bool (*proc_func) (struct ccase *, void *), void *aux)
148 if (proc_func == NULL
149 && case_source_is_class (vfm_source, &storage_source_class)
151 && temporary_trns_chain == NULL
152 && trns_chain_is_empty (permanent_trns_chain))
154 expr_free (process_if_expr);
155 process_if_expr = NULL;
156 dict_set_case_limit (default_dict, 0);
157 dict_clear_vectors (default_dict);
159 update_last_vfm_invocation ();
167 ok = internal_procedure (proc_func, aux);
168 ok = close_active_file () && ok;
174 /* Callback function for multipass_procedure(). */
176 multipass_callback (struct ccase *c, void *cf_)
178 struct casefile *cf = cf_;
179 return casefile_append (cf, c);
182 /* Procedure that allows multiple passes over the input data.
183 The entire active file is passed to PROC_FUNC, with the given
184 AUX as auxiliary data, as a unit. */
186 multipass_procedure (bool (*proc_func) (const struct casefile *, void *aux),
189 if (case_source_is_class (vfm_source, &storage_source_class)
191 && temporary_trns_chain == NULL
192 && trns_chain_is_empty (permanent_trns_chain))
194 proc_func (storage_source_get_casefile (vfm_source), aux);
196 expr_free (process_if_expr);
197 process_if_expr = NULL;
198 dict_set_case_limit (default_dict, 0);
199 dict_clear_vectors (default_dict);
201 update_last_vfm_invocation ();
209 assert (proc_func != NULL);
211 cf = casefile_create (dict_get_next_value_idx (default_dict));
214 ok = internal_procedure (multipass_callback, cf);
215 ok = proc_func (cf, aux) && ok;
216 ok = close_active_file () && ok;
218 casefile_destroy (cf);
224 /* Executes a procedure, as procedure(), except that the caller
225 is responsible for calling open_active_file() and
227 Returns true if successful, false if an I/O error occurred. */
229 internal_procedure (bool (*proc_func) (struct ccase *, void *), void *aux)
231 struct write_case_data wc_data;
234 wc_data.proc_func = proc_func;
236 create_trns_case (&wc_data.trns_case, default_dict);
237 case_create (&wc_data.sink_case, dict_get_next_value_idx (default_dict));
238 wc_data.cases_written = 0;
240 update_last_vfm_invocation ();
242 ok = (vfm_source == NULL
243 || vfm_source->class->read (vfm_source,
245 write_case, &wc_data));
247 case_destroy (&wc_data.sink_case);
248 case_destroy (&wc_data.trns_case);
253 /* Updates last_vfm_invocation. */
255 update_last_vfm_invocation (void)
257 last_vfm_invocation = time (NULL);
260 /* Creates and returns a case, initializing it from the vectors
261 that say which `value's need to be initialized just once, and
262 which ones need to be re-initialized before every case. */
264 create_trns_case (struct ccase *trns_case, struct dictionary *dict)
266 size_t var_cnt = dict_get_var_cnt (dict);
269 case_create (trns_case, dict_get_next_value_idx (dict));
270 for (i = 0; i < var_cnt; i++)
272 struct variable *v = dict_get_var (dict, i);
273 union value *value = case_data_rw (trns_case, v->fv);
275 if (v->type == NUMERIC)
276 value->f = v->leave ? 0.0 : SYSMIS;
278 memset (value->s, ' ', v->width);
282 /* Makes all preparations for reading from the data source and writing
285 open_active_file (void)
287 add_case_limit_trns ();
289 add_process_if_trns ();
291 /* Finalize transformations. */
292 trns_chain_finalize (cur_trns_chain);
294 /* Make permanent_dict refer to the dictionary right before
295 data reaches the sink. */
296 if (permanent_dict == NULL)
297 permanent_dict = default_dict;
299 /* Figure out compaction. */
300 compactor = (dict_needs_compaction (permanent_dict)
301 ? dict_make_compactor (permanent_dict)
305 if (vfm_sink == NULL)
306 vfm_sink = create_case_sink (&storage_sink_class, permanent_dict, NULL);
307 if (vfm_sink->class->open != NULL)
308 vfm_sink->class->open (vfm_sink);
310 /* Allocate memory for lag queue. */
317 lag_queue = xnmalloc (n_lag, sizeof *lag_queue);
318 for (i = 0; i < n_lag; i++)
319 case_nullify (&lag_queue[i]);
323 /* Transforms trns_case and writes it to the replacement active
324 file if advisable. Returns true if more cases can be
325 accepted, false otherwise. Do not call this function again
326 after it has returned false once. */
328 write_case (struct write_case_data *wc_data)
330 enum trns_result retval;
333 /* Execute permanent transformations. */
334 case_nr = wc_data->cases_written + 1;
335 retval = trns_chain_execute (permanent_trns_chain,
336 &wc_data->trns_case, &case_nr);
337 if (retval != TRNS_CONTINUE)
340 /* Write case to LAG queue. */
342 lag_case (&wc_data->trns_case);
344 /* Write case to replacement active file. */
345 wc_data->cases_written++;
346 if (vfm_sink->class->write != NULL)
348 if (compactor != NULL)
350 dict_compactor_compact (compactor, &wc_data->sink_case,
351 &wc_data->trns_case);
352 vfm_sink->class->write (vfm_sink, &wc_data->sink_case);
355 vfm_sink->class->write (vfm_sink, &wc_data->trns_case);
358 /* Execute temporary transformations. */
359 if (temporary_trns_chain != NULL)
361 retval = trns_chain_execute (temporary_trns_chain,
363 &wc_data->cases_written);
364 if (retval != TRNS_CONTINUE)
368 /* Pass case to procedure. */
369 wc_data->cases_analyzed++;
370 if (wc_data->proc_func != NULL)
371 if (!wc_data->proc_func (&wc_data->trns_case, wc_data->aux))
375 clear_case (&wc_data->trns_case);
376 return retval != TRNS_ERROR;
379 /* Add C to the lag queue. */
381 lag_case (const struct ccase *c)
383 if (lag_count < n_lag)
385 case_destroy (&lag_queue[lag_head]);
386 case_clone (&lag_queue[lag_head], c);
387 if (++lag_head >= n_lag)
391 /* Clears the variables in C that need to be cleared between
394 clear_case (struct ccase *c)
396 size_t var_cnt = dict_get_var_cnt (default_dict);
399 for (i = 0; i < var_cnt; i++)
401 struct variable *v = dict_get_var (default_dict, i);
404 if (v->type == NUMERIC)
405 case_data_rw (c, v->fv)->f = SYSMIS;
407 memset (case_data_rw (c, v->fv)->s, ' ', v->width);
412 /* Closes the active file. */
414 close_active_file (void)
416 /* Free memory for lag queue, and turn off lagging. */
421 for (i = 0; i < n_lag; i++)
422 case_destroy (&lag_queue[i]);
427 /* Dictionary from before TEMPORARY becomes permanent. */
428 proc_cancel_temporary_transformations ();
430 /* Finish compaction. */
431 if (compactor != NULL)
433 dict_compactor_destroy (compactor);
434 dict_compact_values (default_dict);
437 /* Free data source. */
438 free_case_source (vfm_source);
441 /* Old data sink becomes new data source. */
442 if (vfm_sink->class->make_source != NULL)
443 vfm_source = vfm_sink->class->make_source (vfm_sink);
444 free_case_sink (vfm_sink);
447 /* Cancel TEMPORARY, PROCESS IF, FILTER, N OF CASES, vectors,
448 and get rid of all the transformations. */
449 dict_clear_vectors (default_dict);
450 permanent_dict = NULL;
451 return proc_cancel_all_transformations ();
454 /* Returns a pointer to the lagged case from N_BEFORE cases before the
455 current one, or NULL if there haven't been that many cases yet. */
457 lagged_case (int n_before)
459 assert (n_before >= 1 );
460 assert (n_before <= n_lag);
462 if (n_before <= lag_count)
464 int index = lag_head - n_before;
467 return &lag_queue[index];
473 /* Represents auxiliary data for handling SPLIT FILE. */
474 struct split_aux_data
476 size_t case_count; /* Number of cases so far. */
477 struct ccase prev_case; /* Data in previous case. */
479 /* Functions to call... */
480 void (*begin_func) (void *); /* ...before data. */
481 bool (*proc_func) (struct ccase *, void *); /* ...with data. */
482 void (*end_func) (void *); /* ...after data. */
483 void *func_aux; /* Auxiliary data. */
486 static int equal_splits (const struct ccase *, const struct ccase *);
487 static bool procedure_with_splits_callback (struct ccase *, void *);
488 static void dump_splits (struct ccase *);
490 /* Like procedure(), but it automatically breaks the case stream
491 into SPLIT FILE break groups. Before each group of cases with
492 identical SPLIT FILE variable values, BEGIN_FUNC is called.
493 Then PROC_FUNC is called with each case in the group.
494 END_FUNC is called when the group is finished. FUNC_AUX is
495 passed to each of the functions as auxiliary data.
497 If the active file is empty, none of BEGIN_FUNC, PROC_FUNC,
498 and END_FUNC will be called at all.
500 If SPLIT FILE is not in effect, then there is one break group
501 (if the active file is nonempty), and BEGIN_FUNC and END_FUNC
504 Returns true if successful, false if an I/O error occurred. */
506 procedure_with_splits (void (*begin_func) (void *aux),
507 bool (*proc_func) (struct ccase *, void *aux),
508 void (*end_func) (void *aux),
511 struct split_aux_data split_aux;
514 split_aux.case_count = 0;
515 case_nullify (&split_aux.prev_case);
516 split_aux.begin_func = begin_func;
517 split_aux.proc_func = proc_func;
518 split_aux.end_func = end_func;
519 split_aux.func_aux = func_aux;
522 ok = internal_procedure (procedure_with_splits_callback, &split_aux);
523 if (split_aux.case_count > 0 && end_func != NULL)
525 if (!close_active_file ())
528 case_destroy (&split_aux.prev_case);
533 /* procedure() callback used by procedure_with_splits(). */
535 procedure_with_splits_callback (struct ccase *c, void *split_aux_)
537 struct split_aux_data *split_aux = split_aux_;
539 /* Start a new series if needed. */
540 if (split_aux->case_count == 0
541 || !equal_splits (c, &split_aux->prev_case))
543 if (split_aux->case_count > 0 && split_aux->end_func != NULL)
544 split_aux->end_func (split_aux->func_aux);
547 case_destroy (&split_aux->prev_case);
548 case_clone (&split_aux->prev_case, c);
550 if (split_aux->begin_func != NULL)
551 split_aux->begin_func (split_aux->func_aux);
554 split_aux->case_count++;
555 if (split_aux->proc_func != NULL)
556 return split_aux->proc_func (c, split_aux->func_aux);
561 /* Compares the SPLIT FILE variables in cases A and B and returns
562 nonzero only if they differ. */
564 equal_splits (const struct ccase *a, const struct ccase *b)
566 return case_compare (a, b,
567 dict_get_split_vars (default_dict),
568 dict_get_split_cnt (default_dict)) == 0;
571 /* Dumps out the values of all the split variables for the case C. */
573 dump_splits (struct ccase *c)
575 struct variable *const *split;
580 split_cnt = dict_get_split_cnt (default_dict);
584 t = tab_create (3, split_cnt + 1, 0);
585 tab_dim (t, tab_natural_dimensions);
586 tab_vline (t, TAL_GAP, 1, 0, split_cnt);
587 tab_vline (t, TAL_GAP, 2, 0, split_cnt);
588 tab_text (t, 0, 0, TAB_NONE, _("Variable"));
589 tab_text (t, 1, 0, TAB_LEFT, _("Value"));
590 tab_text (t, 2, 0, TAB_LEFT, _("Label"));
591 split = dict_get_split_vars (default_dict);
592 for (i = 0; i < split_cnt; i++)
594 struct variable *v = split[i];
598 assert (v->type == NUMERIC || v->type == ALPHA);
599 tab_text (t, 0, i + 1, TAB_LEFT | TAT_PRINTF, "%s", v->name);
601 data_out (temp_buf, &v->print, case_data (c, v->fv));
603 temp_buf[v->print.w] = 0;
604 tab_text (t, 1, i + 1, TAT_PRINTF, "%.*s", v->print.w, temp_buf);
606 val_lab = val_labs_find (v->val_labs, *case_data (c, v->fv));
608 tab_text (t, 2, i + 1, TAB_LEFT, val_lab);
610 tab_flags (t, SOMF_NO_TITLE);
614 /* Represents auxiliary data for handling SPLIT FILE in a
615 multipass procedure. */
616 struct multipass_split_aux_data
618 struct ccase prev_case; /* Data in previous case. */
619 struct casefile *casefile; /* Accumulates data for a split. */
621 /* Function to call with the accumulated data. */
622 bool (*split_func) (const struct casefile *, void *);
623 void *func_aux; /* Auxiliary data. */
626 static bool multipass_split_callback (struct ccase *c, void *aux_);
627 static bool multipass_split_output (struct multipass_split_aux_data *);
629 /* Returns true if successful, false if an I/O error occurred. */
631 multipass_procedure_with_splits (bool (*split_func) (const struct casefile *,
635 struct multipass_split_aux_data aux;
638 assert (split_func != NULL);
642 case_nullify (&aux.prev_case);
644 aux.split_func = split_func;
645 aux.func_aux = func_aux;
647 ok = internal_procedure (multipass_split_callback, &aux);
648 if (aux.casefile != NULL)
649 ok = multipass_split_output (&aux) && ok;
650 case_destroy (&aux.prev_case);
652 if (!close_active_file ())
658 /* procedure() callback used by multipass_procedure_with_splits(). */
660 multipass_split_callback (struct ccase *c, void *aux_)
662 struct multipass_split_aux_data *aux = aux_;
665 /* Start a new series if needed. */
666 if (aux->casefile == NULL || !equal_splits (c, &aux->prev_case))
668 /* Pass any cases to split_func. */
669 if (aux->casefile != NULL)
670 ok = multipass_split_output (aux);
672 /* Start a new casefile. */
673 aux->casefile = casefile_create (dict_get_next_value_idx (default_dict));
675 /* Record split values. */
677 case_destroy (&aux->prev_case);
678 case_clone (&aux->prev_case, c);
681 return casefile_append (aux->casefile, c) && ok;
685 multipass_split_output (struct multipass_split_aux_data *aux)
689 assert (aux->casefile != NULL);
690 ok = aux->split_func (aux->casefile, aux->func_aux);
691 casefile_destroy (aux->casefile);
692 aux->casefile = NULL;
697 /* Discards all the current state in preparation for a data-input
698 command like DATA LIST or GET. */
700 discard_variables (void)
702 dict_clear (default_dict);
703 fh_set_default_handle (NULL);
707 if (vfm_source != NULL)
709 free_case_source (vfm_source);
713 proc_cancel_all_transformations ();
715 expr_free (process_if_expr);
716 process_if_expr = NULL;
718 proc_cancel_temporary_transformations ();
721 /* Returns the current set of permanent transformations,
722 and clears the permanent transformations.
723 For use by INPUT PROGRAM. */
725 proc_capture_transformations (void)
727 struct trns_chain *chain;
729 assert (temporary_trns_chain == NULL);
730 chain = permanent_trns_chain;
731 cur_trns_chain = permanent_trns_chain = trns_chain_create ();
735 /* Adds a transformation that processes a case with PROC and
736 frees itself with FREE to the current set of transformations.
737 The functions are passed AUX as auxiliary data. */
739 add_transformation (trns_proc_func *proc, trns_free_func *free, void *aux)
741 trns_chain_append (cur_trns_chain, NULL, proc, free, aux);
744 /* Adds a transformation that processes a case with PROC and
745 frees itself with FREE to the current set of transformations.
746 When parsing of the block of transformations is complete,
747 FINALIZE will be called.
748 The functions are passed AUX as auxiliary data. */
750 add_transformation_with_finalizer (trns_finalize_func *finalize,
751 trns_proc_func *proc,
752 trns_free_func *free, void *aux)
754 trns_chain_append (cur_trns_chain, finalize, proc, free, aux);
757 /* Returns the index of the next transformation.
758 This value can be returned by a transformation procedure
759 function to indicate a "jump" to that transformation. */
761 next_transformation (void)
763 return trns_chain_next (cur_trns_chain);
766 /* Returns true if the next call to add_transformation() will add
767 a temporary transformation, false if it will add a permanent
770 proc_in_temporary_transformations (void)
772 return temporary_trns_chain != NULL;
775 /* Marks the start of temporary transformations.
776 Further calls to add_transformation() will add temporary
779 proc_start_temporary_transformations (void)
781 if (!proc_in_temporary_transformations ())
783 add_case_limit_trns ();
785 permanent_dict = dict_clone (default_dict);
786 trns_chain_finalize (permanent_trns_chain);
787 temporary_trns_chain = cur_trns_chain = trns_chain_create ();
791 /* Converts all the temporary transformations, if any, to
792 permanent transformations. Further transformations will be
794 Returns true if anything changed, false otherwise. */
796 proc_make_temporary_transformations_permanent (void)
798 if (proc_in_temporary_transformations ())
800 trns_chain_finalize (temporary_trns_chain);
801 trns_chain_splice (permanent_trns_chain, temporary_trns_chain);
802 temporary_trns_chain = NULL;
804 dict_destroy (permanent_dict);
805 permanent_dict = NULL;
813 /* Cancels all temporary transformations, if any. Further
814 transformations will be permanent.
815 Returns true if anything changed, false otherwise. */
817 proc_cancel_temporary_transformations (void)
819 if (proc_in_temporary_transformations ())
821 dict_destroy (default_dict);
822 default_dict = permanent_dict;
823 permanent_dict = NULL;
825 trns_chain_destroy (temporary_trns_chain);
826 temporary_trns_chain = NULL;
834 /* Cancels all transformations, if any.
835 Returns true if successful, false on I/O error. */
837 proc_cancel_all_transformations (void)
840 ok = trns_chain_destroy (permanent_trns_chain);
841 ok = trns_chain_destroy (temporary_trns_chain) && ok;
842 permanent_trns_chain = cur_trns_chain = trns_chain_create ();
843 temporary_trns_chain = NULL;
847 /* Initializes procedure handling. */
851 default_dict = dict_create ();
852 proc_cancel_all_transformations ();
855 /* Finishes up procedure handling. */
859 discard_variables ();
862 /* Sets SINK as the destination for procedure output from the
865 proc_set_sink (struct case_sink *sink)
867 assert (vfm_sink == NULL);
871 /* Sets SOURCE as the source for procedure input for the next
874 proc_set_source (struct case_source *source)
876 assert (vfm_source == NULL);
880 /* Returns true if a source for the next procedure has been
881 configured, false otherwise. */
883 proc_has_source (void)
885 return vfm_source != NULL;
888 /* Returns the output from the previous procedure.
889 For use only immediately after executing a procedure.
890 The returned casefile is owned by the caller; it will not be
891 automatically used for the next procedure's input. */
893 proc_capture_output (void)
895 struct casefile *casefile;
897 /* Try to make sure that this function is called immediately
898 after procedure() or a similar function. */
899 assert (vfm_source != NULL);
900 assert (case_source_is_class (vfm_source, &storage_source_class));
901 assert (trns_chain_is_empty (permanent_trns_chain));
902 assert (!proc_in_temporary_transformations ());
904 casefile = storage_source_decapsulate (vfm_source);
910 static trns_proc_func case_limit_trns_proc;
911 static trns_free_func case_limit_trns_free;
913 /* Adds a transformation that limits the number of cases that may
914 pass through, if default_dict has a case limit. */
916 add_case_limit_trns (void)
918 size_t case_limit = dict_get_case_limit (default_dict);
921 size_t *cases_remaining = xmalloc (sizeof *cases_remaining);
922 *cases_remaining = case_limit;
923 add_transformation (case_limit_trns_proc, case_limit_trns_free,
925 dict_set_case_limit (default_dict, 0);
929 /* Limits the maximum number of cases processed to
932 case_limit_trns_proc (void *cases_remaining_,
933 struct ccase *c UNUSED, int case_nr UNUSED)
935 size_t *cases_remaining = cases_remaining_;
936 if (*cases_remaining > 0)
939 return TRNS_CONTINUE;
942 return TRNS_DROP_CASE;
945 /* Frees the data associated with a case limit transformation. */
947 case_limit_trns_free (void *cases_remaining_)
949 size_t *cases_remaining = cases_remaining_;
950 free (cases_remaining);
954 static trns_proc_func filter_trns_proc;
956 /* Adds a temporary transformation to filter data according to
957 the variable specified on FILTER, if any. */
959 add_filter_trns (void)
961 struct variable *filter_var = dict_get_filter (default_dict);
962 if (filter_var != NULL)
964 proc_start_temporary_transformations ();
965 add_transformation (filter_trns_proc, NULL, filter_var);
969 /* FILTER transformation. */
971 filter_trns_proc (void *filter_var_,
972 struct ccase *c UNUSED, int case_nr UNUSED)
975 struct variable *filter_var = filter_var_;
976 double f = case_num (c, filter_var->fv);
977 return (f != 0.0 && !mv_is_num_missing (&filter_var->miss, f)
978 ? TRNS_CONTINUE : TRNS_DROP_CASE);
981 static trns_proc_func process_if_trns_proc;
982 static trns_free_func process_if_trns_free;
984 /* Adds a temporary transformation to filter data according to
985 the expression specified on PROCESS IF, if any. */
987 add_process_if_trns (void)
989 if (process_if_expr != NULL)
991 proc_start_temporary_transformations ();
992 add_transformation (process_if_trns_proc, process_if_trns_free,
994 process_if_expr = NULL;
998 /* PROCESS IF transformation. */
1000 process_if_trns_proc (void *expression_,
1001 struct ccase *c UNUSED, int case_nr UNUSED)
1004 struct expression *expression = expression_;
1005 return (expr_evaluate_num (expression, c, case_nr) == 1.0
1006 ? TRNS_CONTINUE : TRNS_DROP_CASE);
1009 /* Frees a PROCESS IF transformation. */
1011 process_if_trns_free (void *expression_)
1013 struct expression *expression = expression_;
1014 expr_free (expression);