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
27 #include <data/case-source.h>
28 #include <data/case-sink.h>
29 #include <data/case.h>
30 #include <data/casefile.h>
31 #include <data/dictionary.h>
32 #include <data/file-handle-def.h>
33 #include <data/procedure.h>
34 #include <data/storage-stream.h>
35 #include <data/transformations.h>
36 #include <data/variable.h>
37 #include <language/expressions/public.h>
38 #include <libpspp/alloc.h>
39 #include <libpspp/misc.h>
40 #include <libpspp/str.h>
43 Virtual File Manager (vfm):
45 vfm is used to process data files. It uses the model that
46 data is read from one stream (the data source), processed,
47 then written to another (the data sink). The data source is
48 then deleted and the data sink becomes the data source for the
51 /* Procedure execution data. */
52 struct write_case_data
54 /* Function to call for each case. */
55 bool (*case_func) (struct ccase *, void *); /* Function. */
56 void *aux; /* Auxiliary data. */
58 struct ccase trns_case; /* Case used for transformations. */
59 struct ccase sink_case; /* Case written to sink, if
60 compaction is necessary. */
61 size_t cases_written; /* Cases output so far. */
64 /* Cases are read from vfm_source,
65 pass through permanent_trns_chain (which transforms them into
66 the format described by permanent_dict),
67 are written to vfm_sink,
68 pass through temporary_trns_chain (which transforms them into
69 the format described by default_dict),
70 and are finally passed to the procedure. */
71 static struct case_source *vfm_source;
72 static struct trns_chain *permanent_trns_chain;
73 static struct dictionary *permanent_dict;
74 static struct case_sink *vfm_sink;
75 static struct trns_chain *temporary_trns_chain;
76 struct dictionary *default_dict;
78 /* The transformation chain that the next transformation will be
80 static struct trns_chain *cur_trns_chain;
82 /* The compactor used to compact a case, if necessary;
83 otherwise a null pointer. */
84 static struct dict_compactor *compactor;
86 /* Time at which vfm was last invoked. */
87 static time_t last_vfm_invocation;
90 int n_lag; /* Number of cases to lag. */
91 static int lag_count; /* Number of cases in lag_queue so far. */
92 static int lag_head; /* Index where next case will be added. */
93 static struct ccase *lag_queue; /* Array of n_lag ccase * elements. */
95 static void add_case_limit_trns (void);
96 static void add_filter_trns (void);
97 static void add_process_if_trns (void);
99 static bool internal_procedure (bool (*case_func) (struct ccase *, void *),
100 bool (*end_func) (void *),
102 static void update_last_vfm_invocation (void);
103 static void create_trns_case (struct ccase *, struct dictionary *);
104 static void open_active_file (void);
105 static bool write_case (struct write_case_data *wc_data);
106 static void lag_case (const struct ccase *c);
107 static void clear_case (struct ccase *c);
108 static bool close_active_file (void);
110 /* Public functions. */
112 /* Returns the last time the data was read. */
114 time_of_last_procedure (void)
116 if (last_vfm_invocation == 0)
117 update_last_vfm_invocation ();
118 return last_vfm_invocation;
121 /* Regular procedure. */
123 /* Reads the data from the input program and writes it to a new
124 active file. For each case we read from the input program, we
127 1. Execute permanent transformations. If these drop the case,
128 start the next case from step 1.
130 2. Write case to replacement active file.
132 3. Execute temporary transformations. If these drop the case,
133 start the next case from step 1.
135 4. Pass case to PROC_FUNC, passing AUX as auxiliary data.
137 Returns true if successful, false if an I/O error occurred. */
139 procedure (bool (*proc_func) (struct ccase *, void *), void *aux)
141 return internal_procedure (proc_func, NULL, aux);
144 /* Multipass procedure. */
146 struct multipass_aux_data
148 struct casefile *casefile;
150 bool (*proc_func) (const struct casefile *, void *aux);
154 /* Case processing function for multipass_procedure(). */
156 multipass_case_func (struct ccase *c, void *aux_data_)
158 struct multipass_aux_data *aux_data = aux_data_;
159 return casefile_append (aux_data->casefile, c);
162 /* End-of-file function for multipass_procedure(). */
164 multipass_end_func (void *aux_data_)
166 struct multipass_aux_data *aux_data = aux_data_;
167 return (aux_data->proc_func == NULL
168 || aux_data->proc_func (aux_data->casefile, aux_data->aux));
171 /* Procedure that allows multiple passes over the input data.
172 The entire active file is passed to PROC_FUNC, with the given
173 AUX as auxiliary data, as a unit. */
175 multipass_procedure (bool (*proc_func) (const struct casefile *, void *aux),
178 struct multipass_aux_data aux_data;
181 aux_data.casefile = casefile_create (dict_get_next_value_idx (default_dict));
182 aux_data.proc_func = proc_func;
185 ok = internal_procedure (multipass_case_func, multipass_end_func, &aux_data);
186 ok = !casefile_error (aux_data.casefile) && ok;
188 casefile_destroy (aux_data.casefile);
193 /* Procedure implementation. */
195 /* Executes a procedure.
196 Passes each case to CASE_FUNC.
197 Calls END_FUNC after the last case.
198 Returns true if successful, false if an I/O error occurred (or
199 if CASE_FUNC or END_FUNC ever returned false). */
201 internal_procedure (bool (*case_func) (struct ccase *, void *),
202 bool (*end_func) (void *),
205 struct write_case_data wc_data;
208 assert (vfm_source != NULL);
210 update_last_vfm_invocation ();
212 /* Optimize the trivial case where we're not going to do
213 anything with the data, by not reading the data at all. */
214 if (case_func == NULL && end_func == NULL
215 && case_source_is_class (vfm_source, &storage_source_class)
217 && (temporary_trns_chain == NULL
218 || trns_chain_is_empty (temporary_trns_chain))
219 && trns_chain_is_empty (permanent_trns_chain))
222 expr_free (process_if_expr);
223 process_if_expr = NULL;
224 dict_set_case_limit (default_dict, 0);
225 dict_clear_vectors (default_dict);
231 wc_data.case_func = case_func;
233 create_trns_case (&wc_data.trns_case, default_dict);
234 case_create (&wc_data.sink_case, dict_get_next_value_idx (default_dict));
235 wc_data.cases_written = 0;
237 ok = vfm_source->class->read (vfm_source,
239 write_case, &wc_data) && ok;
240 if (end_func != NULL)
241 ok = end_func (aux) && ok;
243 case_destroy (&wc_data.sink_case);
244 case_destroy (&wc_data.trns_case);
246 ok = close_active_file () && ok;
251 /* Updates last_vfm_invocation. */
253 update_last_vfm_invocation (void)
255 last_vfm_invocation = time (NULL);
258 /* Creates and returns a case, initializing it from the vectors
259 that say which `value's need to be initialized just once, and
260 which ones need to be re-initialized before every case. */
262 create_trns_case (struct ccase *trns_case, struct dictionary *dict)
264 size_t var_cnt = dict_get_var_cnt (dict);
267 case_create (trns_case, dict_get_next_value_idx (dict));
268 for (i = 0; i < var_cnt; i++)
270 struct variable *v = dict_get_var (dict, i);
271 union value *value = case_data_rw (trns_case, v->fv);
273 if (v->type == NUMERIC)
274 value->f = v->leave ? 0.0 : SYSMIS;
276 memset (value->s, ' ', v->width);
280 /* Makes all preparations for reading from the data source and writing
283 open_active_file (void)
285 add_case_limit_trns ();
287 add_process_if_trns ();
289 /* Finalize transformations. */
290 trns_chain_finalize (cur_trns_chain);
292 /* Make permanent_dict refer to the dictionary right before
293 data reaches the sink. */
294 if (permanent_dict == NULL)
295 permanent_dict = default_dict;
297 /* Figure out compaction. */
298 compactor = (dict_needs_compaction (permanent_dict)
299 ? dict_make_compactor (permanent_dict)
303 if (vfm_sink == NULL)
304 vfm_sink = create_case_sink (&storage_sink_class, permanent_dict, NULL);
305 if (vfm_sink->class->open != NULL)
306 vfm_sink->class->open (vfm_sink);
308 /* Allocate memory for lag queue. */
315 lag_queue = xnmalloc (n_lag, sizeof *lag_queue);
316 for (i = 0; i < n_lag; i++)
317 case_nullify (&lag_queue[i]);
321 /* Transforms trns_case and writes it to the replacement active
322 file if advisable. Returns true if more cases can be
323 accepted, false otherwise. Do not call this function again
324 after it has returned false once. */
326 write_case (struct write_case_data *wc_data)
328 enum trns_result retval;
331 /* Execute permanent transformations. */
332 case_nr = wc_data->cases_written + 1;
333 retval = trns_chain_execute (permanent_trns_chain,
334 &wc_data->trns_case, &case_nr);
335 if (retval != TRNS_CONTINUE)
338 /* Write case to LAG queue. */
340 lag_case (&wc_data->trns_case);
342 /* Write case to replacement active file. */
343 wc_data->cases_written++;
344 if (vfm_sink->class->write != NULL)
346 if (compactor != NULL)
348 dict_compactor_compact (compactor, &wc_data->sink_case,
349 &wc_data->trns_case);
350 vfm_sink->class->write (vfm_sink, &wc_data->sink_case);
353 vfm_sink->class->write (vfm_sink, &wc_data->trns_case);
356 /* Execute temporary transformations. */
357 if (temporary_trns_chain != NULL)
359 retval = trns_chain_execute (temporary_trns_chain,
361 &wc_data->cases_written);
362 if (retval != TRNS_CONTINUE)
366 /* Pass case to procedure. */
367 if (wc_data->case_func != NULL)
368 if (!wc_data->case_func (&wc_data->trns_case, wc_data->aux))
372 clear_case (&wc_data->trns_case);
373 return retval != TRNS_ERROR;
376 /* Add C to the lag queue. */
378 lag_case (const struct ccase *c)
380 if (lag_count < n_lag)
382 case_destroy (&lag_queue[lag_head]);
383 case_clone (&lag_queue[lag_head], c);
384 if (++lag_head >= n_lag)
388 /* Clears the variables in C that need to be cleared between
391 clear_case (struct ccase *c)
393 size_t var_cnt = dict_get_var_cnt (default_dict);
396 for (i = 0; i < var_cnt; i++)
398 struct variable *v = dict_get_var (default_dict, i);
401 if (v->type == NUMERIC)
402 case_data_rw (c, v->fv)->f = SYSMIS;
404 memset (case_data_rw (c, v->fv)->s, ' ', v->width);
409 /* Closes the active file. */
411 close_active_file (void)
413 /* Free memory for lag queue, and turn off lagging. */
418 for (i = 0; i < n_lag; i++)
419 case_destroy (&lag_queue[i]);
424 /* Dictionary from before TEMPORARY becomes permanent. */
425 proc_cancel_temporary_transformations ();
427 /* Finish compaction. */
428 if (compactor != NULL)
430 dict_compactor_destroy (compactor);
431 dict_compact_values (default_dict);
435 /* Free data source. */
436 free_case_source (vfm_source);
439 /* Old data sink becomes new data source. */
440 if (vfm_sink->class->make_source != NULL)
441 vfm_source = vfm_sink->class->make_source (vfm_sink);
442 free_case_sink (vfm_sink);
445 /* Cancel TEMPORARY, PROCESS IF, FILTER, N OF CASES, vectors,
446 and get rid of all the transformations. */
447 dict_clear_vectors (default_dict);
448 permanent_dict = NULL;
449 return proc_cancel_all_transformations ();
452 /* Returns a pointer to the lagged case from N_BEFORE cases before the
453 current one, or NULL if there haven't been that many cases yet. */
455 lagged_case (int n_before)
457 assert (n_before >= 1 );
458 assert (n_before <= n_lag);
460 if (n_before <= lag_count)
462 int index = lag_head - n_before;
465 return &lag_queue[index];
471 /* Procedure that separates the data into SPLIT FILE groups. */
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 /* Callback functions. */
480 void (*begin_func) (const struct ccase *, void *);
481 bool (*proc_func) (const struct ccase *, void *);
482 void (*end_func) (void *);
486 static int equal_splits (const struct ccase *, const struct ccase *);
487 static bool split_procedure_case_func (struct ccase *c, void *split_aux_);
488 static bool split_procedure_end_func (void *split_aux_);
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 with the first case in the group.
494 Then PROC_FUNC is called for each case in the group (including
496 END_FUNC is called when the group is finished. FUNC_AUX is
497 passed to each of the functions as auxiliary data.
499 If the active file is empty, none of BEGIN_FUNC, PROC_FUNC,
500 and END_FUNC will be called at all.
502 If SPLIT FILE is not in effect, then there is one break group
503 (if the active file is nonempty), and BEGIN_FUNC and END_FUNC
506 Returns true if successful, false if an I/O error occurred. */
508 procedure_with_splits (void (*begin_func) (const struct ccase *, void *aux),
509 bool (*proc_func) (const struct ccase *, void *aux),
510 void (*end_func) (void *aux),
513 struct split_aux_data split_aux;
516 split_aux.case_count = 0;
517 case_nullify (&split_aux.prev_case);
518 split_aux.begin_func = begin_func;
519 split_aux.proc_func = proc_func;
520 split_aux.end_func = end_func;
521 split_aux.func_aux = func_aux;
523 ok = internal_procedure (split_procedure_case_func,
524 split_procedure_end_func, &split_aux);
526 case_destroy (&split_aux.prev_case);
531 /* Case callback used by procedure_with_splits(). */
533 split_procedure_case_func (struct ccase *c, void *split_aux_)
535 struct split_aux_data *split_aux = split_aux_;
537 /* Start a new series if needed. */
538 if (split_aux->case_count == 0
539 || !equal_splits (c, &split_aux->prev_case))
541 if (split_aux->case_count > 0 && split_aux->end_func != NULL)
542 split_aux->end_func (split_aux->func_aux);
544 case_destroy (&split_aux->prev_case);
545 case_clone (&split_aux->prev_case, c);
547 if (split_aux->begin_func != NULL)
548 split_aux->begin_func (&split_aux->prev_case, split_aux->func_aux);
551 split_aux->case_count++;
552 return (split_aux->proc_func == NULL
553 || split_aux->proc_func (c, split_aux->func_aux));
556 /* End-of-file callback used by procedure_with_splits(). */
558 split_procedure_end_func (void *split_aux_)
560 struct split_aux_data *split_aux = split_aux_;
562 if (split_aux->case_count > 0 && split_aux->end_func != NULL)
563 split_aux->end_func (split_aux->func_aux);
567 /* Compares the SPLIT FILE variables in cases A and B and returns
568 nonzero only if they differ. */
570 equal_splits (const struct ccase *a, const struct ccase *b)
572 return case_compare (a, b,
573 dict_get_split_vars (default_dict),
574 dict_get_split_cnt (default_dict)) == 0;
577 /* Multipass procedure that separates the data into SPLIT FILE
580 /* Represents auxiliary data for handling SPLIT FILE in a
581 multipass procedure. */
582 struct multipass_split_aux_data
584 struct ccase prev_case; /* Data in previous case. */
585 struct casefile *casefile; /* Accumulates data for a split. */
587 /* Function to call with the accumulated data. */
588 bool (*split_func) (const struct ccase *first, const struct casefile *,
590 void *func_aux; /* Auxiliary data. */
593 static bool multipass_split_case_func (struct ccase *c, void *aux_);
594 static bool multipass_split_end_func (void *aux_);
595 static bool multipass_split_output (struct multipass_split_aux_data *);
597 /* Returns true if successful, false if an I/O error occurred. */
599 multipass_procedure_with_splits (bool (*split_func) (const struct ccase *first,
600 const struct casefile *,
604 struct multipass_split_aux_data aux;
607 case_nullify (&aux.prev_case);
609 aux.split_func = split_func;
610 aux.func_aux = func_aux;
612 ok = internal_procedure (multipass_split_case_func,
613 multipass_split_end_func, &aux);
614 case_destroy (&aux.prev_case);
619 /* Case callback used by multipass_procedure_with_splits(). */
621 multipass_split_case_func (struct ccase *c, void *aux_)
623 struct multipass_split_aux_data *aux = aux_;
626 /* Start a new series if needed. */
627 if (aux->casefile == NULL || !equal_splits (c, &aux->prev_case))
629 /* Record split values. */
630 case_destroy (&aux->prev_case);
631 case_clone (&aux->prev_case, c);
633 /* Pass any cases to split_func. */
634 if (aux->casefile != NULL)
635 ok = multipass_split_output (aux);
637 /* Start a new casefile. */
638 aux->casefile = casefile_create (dict_get_next_value_idx (default_dict));
641 return casefile_append (aux->casefile, c) && ok;
644 /* End-of-file callback used by multipass_procedure_with_splits(). */
646 multipass_split_end_func (void *aux_)
648 struct multipass_split_aux_data *aux = aux_;
649 return (aux->casefile == NULL || multipass_split_output (aux));
653 multipass_split_output (struct multipass_split_aux_data *aux)
657 assert (aux->casefile != NULL);
658 ok = aux->split_func (&aux->prev_case, aux->casefile, aux->func_aux);
659 casefile_destroy (aux->casefile);
660 aux->casefile = NULL;
665 /* Discards all the current state in preparation for a data-input
666 command like DATA LIST or GET. */
668 discard_variables (void)
670 dict_clear (default_dict);
671 fh_set_default_handle (NULL);
675 free_case_source (vfm_source);
678 proc_cancel_all_transformations ();
680 expr_free (process_if_expr);
681 process_if_expr = NULL;
683 proc_cancel_temporary_transformations ();
686 /* Returns the current set of permanent transformations,
687 and clears the permanent transformations.
688 For use by INPUT PROGRAM. */
690 proc_capture_transformations (void)
692 struct trns_chain *chain;
694 assert (temporary_trns_chain == NULL);
695 chain = permanent_trns_chain;
696 cur_trns_chain = permanent_trns_chain = trns_chain_create ();
700 /* Adds a transformation that processes a case with PROC and
701 frees itself with FREE to the current set of transformations.
702 The functions are passed AUX as auxiliary data. */
704 add_transformation (trns_proc_func *proc, trns_free_func *free, void *aux)
706 trns_chain_append (cur_trns_chain, NULL, proc, free, aux);
709 /* Adds a transformation that processes a case with PROC and
710 frees itself with FREE to the current set of transformations.
711 When parsing of the block of transformations is complete,
712 FINALIZE will be called.
713 The functions are passed AUX as auxiliary data. */
715 add_transformation_with_finalizer (trns_finalize_func *finalize,
716 trns_proc_func *proc,
717 trns_free_func *free, void *aux)
719 trns_chain_append (cur_trns_chain, finalize, proc, free, aux);
722 /* Returns the index of the next transformation.
723 This value can be returned by a transformation procedure
724 function to indicate a "jump" to that transformation. */
726 next_transformation (void)
728 return trns_chain_next (cur_trns_chain);
731 /* Returns true if the next call to add_transformation() will add
732 a temporary transformation, false if it will add a permanent
735 proc_in_temporary_transformations (void)
737 return temporary_trns_chain != NULL;
740 /* Marks the start of temporary transformations.
741 Further calls to add_transformation() will add temporary
744 proc_start_temporary_transformations (void)
746 if (!proc_in_temporary_transformations ())
748 add_case_limit_trns ();
750 permanent_dict = dict_clone (default_dict);
751 trns_chain_finalize (permanent_trns_chain);
752 temporary_trns_chain = cur_trns_chain = trns_chain_create ();
756 /* Converts all the temporary transformations, if any, to
757 permanent transformations. Further transformations will be
759 Returns true if anything changed, false otherwise. */
761 proc_make_temporary_transformations_permanent (void)
763 if (proc_in_temporary_transformations ())
765 trns_chain_finalize (temporary_trns_chain);
766 trns_chain_splice (permanent_trns_chain, temporary_trns_chain);
767 temporary_trns_chain = NULL;
769 dict_destroy (permanent_dict);
770 permanent_dict = NULL;
778 /* Cancels all temporary transformations, if any. Further
779 transformations will be permanent.
780 Returns true if anything changed, false otherwise. */
782 proc_cancel_temporary_transformations (void)
784 if (proc_in_temporary_transformations ())
786 dict_destroy (default_dict);
787 default_dict = permanent_dict;
788 permanent_dict = NULL;
790 trns_chain_destroy (temporary_trns_chain);
791 temporary_trns_chain = NULL;
799 /* Cancels all transformations, if any.
800 Returns true if successful, false on I/O error. */
802 proc_cancel_all_transformations (void)
805 ok = trns_chain_destroy (permanent_trns_chain);
806 ok = trns_chain_destroy (temporary_trns_chain) && ok;
807 permanent_trns_chain = cur_trns_chain = trns_chain_create ();
808 temporary_trns_chain = NULL;
812 /* Initializes procedure handling. */
816 default_dict = dict_create ();
817 proc_cancel_all_transformations ();
820 /* Finishes up procedure handling. */
824 discard_variables ();
827 /* Sets SINK as the destination for procedure output from the
830 proc_set_sink (struct case_sink *sink)
832 assert (vfm_sink == NULL);
836 /* Sets SOURCE as the source for procedure input for the next
839 proc_set_source (struct case_source *source)
841 assert (vfm_source == NULL);
845 /* Returns true if a source for the next procedure has been
846 configured, false otherwise. */
848 proc_has_source (void)
850 return vfm_source != NULL;
853 /* Returns the output from the previous procedure.
854 For use only immediately after executing a procedure.
855 The returned casefile is owned by the caller; it will not be
856 automatically used for the next procedure's input. */
858 proc_capture_output (void)
860 struct casefile *casefile;
862 /* Try to make sure that this function is called immediately
863 after procedure() or a similar function. */
864 assert (vfm_source != NULL);
865 assert (case_source_is_class (vfm_source, &storage_source_class));
866 assert (trns_chain_is_empty (permanent_trns_chain));
867 assert (!proc_in_temporary_transformations ());
869 casefile = storage_source_decapsulate (vfm_source);
875 static trns_proc_func case_limit_trns_proc;
876 static trns_free_func case_limit_trns_free;
878 /* Adds a transformation that limits the number of cases that may
879 pass through, if default_dict has a case limit. */
881 add_case_limit_trns (void)
883 size_t case_limit = dict_get_case_limit (default_dict);
886 size_t *cases_remaining = xmalloc (sizeof *cases_remaining);
887 *cases_remaining = case_limit;
888 add_transformation (case_limit_trns_proc, case_limit_trns_free,
890 dict_set_case_limit (default_dict, 0);
894 /* Limits the maximum number of cases processed to
897 case_limit_trns_proc (void *cases_remaining_,
898 struct ccase *c UNUSED, int case_nr UNUSED)
900 size_t *cases_remaining = cases_remaining_;
901 if (*cases_remaining > 0)
904 return TRNS_CONTINUE;
907 return TRNS_DROP_CASE;
910 /* Frees the data associated with a case limit transformation. */
912 case_limit_trns_free (void *cases_remaining_)
914 size_t *cases_remaining = cases_remaining_;
915 free (cases_remaining);
919 static trns_proc_func filter_trns_proc;
921 /* Adds a temporary transformation to filter data according to
922 the variable specified on FILTER, if any. */
924 add_filter_trns (void)
926 struct variable *filter_var = dict_get_filter (default_dict);
927 if (filter_var != NULL)
929 proc_start_temporary_transformations ();
930 add_transformation (filter_trns_proc, NULL, filter_var);
934 /* FILTER transformation. */
936 filter_trns_proc (void *filter_var_,
937 struct ccase *c UNUSED, int case_nr UNUSED)
940 struct variable *filter_var = filter_var_;
941 double f = case_num (c, filter_var->fv);
942 return (f != 0.0 && !mv_is_num_missing (&filter_var->miss, f)
943 ? TRNS_CONTINUE : TRNS_DROP_CASE);
946 static trns_proc_func process_if_trns_proc;
947 static trns_free_func process_if_trns_free;
949 /* Adds a temporary transformation to filter data according to
950 the expression specified on PROCESS IF, if any. */
952 add_process_if_trns (void)
954 if (process_if_expr != NULL)
956 proc_start_temporary_transformations ();
957 add_transformation (process_if_trns_proc, process_if_trns_free,
959 process_if_expr = NULL;
963 /* PROCESS IF transformation. */
965 process_if_trns_proc (void *expression_,
966 struct ccase *c UNUSED, int case_nr UNUSED)
969 struct expression *expression = expression_;
970 return (expr_evaluate_num (expression, c, case_nr) == 1.0
971 ? TRNS_CONTINUE : TRNS_DROP_CASE);
974 /* Frees a PROCESS IF transformation. */
976 process_if_trns_free (void *expression_)
978 struct expression *expression = expression_;
979 expr_free (expression);