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) (const struct ccase *, void *);
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) (const struct ccase *,
101 bool (*end_func) (void *),
103 static void update_last_vfm_invocation (void);
104 static void create_trns_case (struct ccase *, struct dictionary *);
105 static void open_active_file (void);
106 static bool write_case (struct write_case_data *wc_data);
107 static void lag_case (const struct ccase *c);
108 static void clear_case (struct ccase *c);
109 static bool close_active_file (void);
111 /* Public functions. */
113 /* Returns the last time the data was read. */
115 time_of_last_procedure (void)
117 if (last_vfm_invocation == 0)
118 update_last_vfm_invocation ();
119 return last_vfm_invocation;
122 /* Regular procedure. */
124 /* Reads the data from the input program and writes it to a new
125 active file. For each case we read from the input program, we
128 1. Execute permanent transformations. If these drop the case,
129 start the next case from step 1.
131 2. Write case to replacement active file.
133 3. Execute temporary transformations. If these drop the case,
134 start the next case from step 1.
136 4. Pass case to PROC_FUNC, passing AUX as auxiliary data.
138 Returns true if successful, false if an I/O error occurred. */
140 procedure (bool (*proc_func) (const struct ccase *, void *), void *aux)
142 return internal_procedure (proc_func, NULL, aux);
145 /* Multipass procedure. */
147 struct multipass_aux_data
149 struct casefile *casefile;
151 bool (*proc_func) (const struct casefile *, void *aux);
155 /* Case processing function for multipass_procedure(). */
157 multipass_case_func (const struct ccase *c, void *aux_data_)
159 struct multipass_aux_data *aux_data = aux_data_;
160 return casefile_append (aux_data->casefile, c);
163 /* End-of-file function for multipass_procedure(). */
165 multipass_end_func (void *aux_data_)
167 struct multipass_aux_data *aux_data = aux_data_;
168 return (aux_data->proc_func == NULL
169 || aux_data->proc_func (aux_data->casefile, aux_data->aux));
172 /* Procedure that allows multiple passes over the input data.
173 The entire active file is passed to PROC_FUNC, with the given
174 AUX as auxiliary data, as a unit. */
176 multipass_procedure (bool (*proc_func) (const struct casefile *, void *aux),
179 struct multipass_aux_data aux_data;
182 aux_data.casefile = casefile_create (dict_get_next_value_idx (default_dict));
183 aux_data.proc_func = proc_func;
186 ok = internal_procedure (multipass_case_func, multipass_end_func, &aux_data);
187 ok = !casefile_error (aux_data.casefile) && ok;
189 casefile_destroy (aux_data.casefile);
194 /* Procedure implementation. */
196 /* Executes a procedure.
197 Passes each case to CASE_FUNC.
198 Calls END_FUNC after the last case.
199 Returns true if successful, false if an I/O error occurred (or
200 if CASE_FUNC or END_FUNC ever returned false). */
202 internal_procedure (bool (*case_func) (const struct ccase *, void *),
203 bool (*end_func) (void *),
206 struct write_case_data wc_data;
209 assert (vfm_source != NULL);
211 update_last_vfm_invocation ();
213 /* Optimize the trivial case where we're not going to do
214 anything with the data, by not reading the data at all. */
215 if (case_func == NULL && end_func == NULL
216 && case_source_is_class (vfm_source, &storage_source_class)
218 && (temporary_trns_chain == NULL
219 || trns_chain_is_empty (temporary_trns_chain))
220 && trns_chain_is_empty (permanent_trns_chain))
223 expr_free (process_if_expr);
224 process_if_expr = NULL;
225 dict_set_case_limit (default_dict, 0);
226 dict_clear_vectors (default_dict);
232 wc_data.case_func = case_func;
234 create_trns_case (&wc_data.trns_case, default_dict);
235 case_create (&wc_data.sink_case, dict_get_next_value_idx (default_dict));
236 wc_data.cases_written = 0;
238 ok = vfm_source->class->read (vfm_source,
240 write_case, &wc_data) && ok;
241 if (end_func != NULL)
242 ok = end_func (aux) && ok;
244 case_destroy (&wc_data.sink_case);
245 case_destroy (&wc_data.trns_case);
247 ok = close_active_file () && ok;
252 /* Updates last_vfm_invocation. */
254 update_last_vfm_invocation (void)
256 last_vfm_invocation = time (NULL);
259 /* Creates and returns a case, initializing it from the vectors
260 that say which `value's need to be initialized just once, and
261 which ones need to be re-initialized before every case. */
263 create_trns_case (struct ccase *trns_case, struct dictionary *dict)
265 size_t var_cnt = dict_get_var_cnt (dict);
268 case_create (trns_case, dict_get_next_value_idx (dict));
269 for (i = 0; i < var_cnt; i++)
271 struct variable *v = dict_get_var (dict, i);
272 union value *value = case_data_rw (trns_case, v->fv);
274 if (v->type == NUMERIC)
275 value->f = v->leave ? 0.0 : SYSMIS;
277 memset (value->s, ' ', v->width);
281 /* Makes all preparations for reading from the data source and writing
284 open_active_file (void)
286 add_case_limit_trns ();
288 add_process_if_trns ();
290 /* Finalize transformations. */
291 trns_chain_finalize (cur_trns_chain);
293 /* Make permanent_dict refer to the dictionary right before
294 data reaches the sink. */
295 if (permanent_dict == NULL)
296 permanent_dict = default_dict;
298 /* Figure out compaction. */
299 compactor = (dict_needs_compaction (permanent_dict)
300 ? dict_make_compactor (permanent_dict)
304 if (vfm_sink == NULL)
305 vfm_sink = create_case_sink (&storage_sink_class, permanent_dict, NULL);
306 if (vfm_sink->class->open != NULL)
307 vfm_sink->class->open (vfm_sink);
309 /* Allocate memory for lag queue. */
316 lag_queue = xnmalloc (n_lag, sizeof *lag_queue);
317 for (i = 0; i < n_lag; i++)
318 case_nullify (&lag_queue[i]);
322 /* Transforms trns_case and writes it to the replacement active
323 file if advisable. Returns true if more cases can be
324 accepted, false otherwise. Do not call this function again
325 after it has returned false once. */
327 write_case (struct write_case_data *wc_data)
329 enum trns_result retval;
332 /* Execute permanent transformations. */
333 case_nr = wc_data->cases_written + 1;
334 retval = trns_chain_execute (permanent_trns_chain,
335 &wc_data->trns_case, &case_nr);
336 if (retval != TRNS_CONTINUE)
339 /* Write case to LAG queue. */
341 lag_case (&wc_data->trns_case);
343 /* Write case to replacement active file. */
344 wc_data->cases_written++;
345 if (vfm_sink->class->write != NULL)
347 if (compactor != NULL)
349 dict_compactor_compact (compactor, &wc_data->sink_case,
350 &wc_data->trns_case);
351 vfm_sink->class->write (vfm_sink, &wc_data->sink_case);
354 vfm_sink->class->write (vfm_sink, &wc_data->trns_case);
357 /* Execute temporary transformations. */
358 if (temporary_trns_chain != NULL)
360 retval = trns_chain_execute (temporary_trns_chain,
362 &wc_data->cases_written);
363 if (retval != TRNS_CONTINUE)
367 /* Pass case to procedure. */
368 if (wc_data->case_func != NULL)
369 if (!wc_data->case_func (&wc_data->trns_case, wc_data->aux))
373 clear_case (&wc_data->trns_case);
374 return retval != TRNS_ERROR;
377 /* Add C to the lag queue. */
379 lag_case (const struct ccase *c)
381 if (lag_count < n_lag)
383 case_destroy (&lag_queue[lag_head]);
384 case_clone (&lag_queue[lag_head], c);
385 if (++lag_head >= n_lag)
389 /* Clears the variables in C that need to be cleared between
392 clear_case (struct ccase *c)
394 size_t var_cnt = dict_get_var_cnt (default_dict);
397 for (i = 0; i < var_cnt; i++)
399 struct variable *v = dict_get_var (default_dict, i);
402 if (v->type == NUMERIC)
403 case_data_rw (c, v->fv)->f = SYSMIS;
405 memset (case_data_rw (c, v->fv)->s, ' ', v->width);
410 /* Closes the active file. */
412 close_active_file (void)
414 /* Free memory for lag queue, and turn off lagging. */
419 for (i = 0; i < n_lag; i++)
420 case_destroy (&lag_queue[i]);
425 /* Dictionary from before TEMPORARY becomes permanent. */
426 proc_cancel_temporary_transformations ();
428 /* Finish compaction. */
429 if (compactor != NULL)
431 dict_compactor_destroy (compactor);
432 dict_compact_values (default_dict);
436 /* Free data source. */
437 free_case_source (vfm_source);
440 /* Old data sink becomes new data source. */
441 if (vfm_sink->class->make_source != NULL)
442 vfm_source = vfm_sink->class->make_source (vfm_sink);
443 free_case_sink (vfm_sink);
446 /* Cancel TEMPORARY, PROCESS IF, FILTER, N OF CASES, vectors,
447 and get rid of all the transformations. */
448 dict_clear_vectors (default_dict);
449 permanent_dict = NULL;
450 return proc_cancel_all_transformations ();
453 /* Returns a pointer to the lagged case from N_BEFORE cases before the
454 current one, or NULL if there haven't been that many cases yet. */
456 lagged_case (int n_before)
458 assert (n_before >= 1 );
459 assert (n_before <= n_lag);
461 if (n_before <= lag_count)
463 int index = lag_head - n_before;
466 return &lag_queue[index];
472 /* Procedure that separates the data into SPLIT FILE groups. */
474 /* Represents auxiliary data for handling SPLIT FILE. */
475 struct split_aux_data
477 size_t case_count; /* Number of cases so far. */
478 struct ccase prev_case; /* Data in previous case. */
480 /* Callback functions. */
481 void (*begin_func) (const struct ccase *, void *);
482 bool (*proc_func) (const struct ccase *, void *);
483 void (*end_func) (void *);
487 static int equal_splits (const struct ccase *, const struct ccase *);
488 static bool split_procedure_case_func (const struct ccase *c, void *);
489 static bool split_procedure_end_func (void *);
491 /* Like procedure(), but it automatically breaks the case stream
492 into SPLIT FILE break groups. Before each group of cases with
493 identical SPLIT FILE variable values, BEGIN_FUNC is called
494 with the first case in the group.
495 Then PROC_FUNC is called for each case in the group (including
497 END_FUNC is called when the group is finished. FUNC_AUX is
498 passed to each of the functions as auxiliary data.
500 If the active file is empty, none of BEGIN_FUNC, PROC_FUNC,
501 and END_FUNC will be called at all.
503 If SPLIT FILE is not in effect, then there is one break group
504 (if the active file is nonempty), and BEGIN_FUNC and END_FUNC
507 Returns true if successful, false if an I/O error occurred. */
509 procedure_with_splits (void (*begin_func) (const struct ccase *, void *aux),
510 bool (*proc_func) (const struct ccase *, void *aux),
511 void (*end_func) (void *aux),
514 struct split_aux_data split_aux;
517 split_aux.case_count = 0;
518 case_nullify (&split_aux.prev_case);
519 split_aux.begin_func = begin_func;
520 split_aux.proc_func = proc_func;
521 split_aux.end_func = end_func;
522 split_aux.func_aux = func_aux;
524 ok = internal_procedure (split_procedure_case_func,
525 split_procedure_end_func, &split_aux);
527 case_destroy (&split_aux.prev_case);
532 /* Case callback used by procedure_with_splits(). */
534 split_procedure_case_func (const struct ccase *c, void *split_aux_)
536 struct split_aux_data *split_aux = split_aux_;
538 /* Start a new series if needed. */
539 if (split_aux->case_count == 0
540 || !equal_splits (c, &split_aux->prev_case))
542 if (split_aux->case_count > 0 && split_aux->end_func != NULL)
543 split_aux->end_func (split_aux->func_aux);
545 case_destroy (&split_aux->prev_case);
546 case_clone (&split_aux->prev_case, c);
548 if (split_aux->begin_func != NULL)
549 split_aux->begin_func (&split_aux->prev_case, split_aux->func_aux);
552 split_aux->case_count++;
553 return (split_aux->proc_func == NULL
554 || split_aux->proc_func (c, split_aux->func_aux));
557 /* End-of-file callback used by procedure_with_splits(). */
559 split_procedure_end_func (void *split_aux_)
561 struct split_aux_data *split_aux = split_aux_;
563 if (split_aux->case_count > 0 && split_aux->end_func != NULL)
564 split_aux->end_func (split_aux->func_aux);
568 /* Compares the SPLIT FILE variables in cases A and B and returns
569 nonzero only if they differ. */
571 equal_splits (const struct ccase *a, const struct ccase *b)
573 return case_compare (a, b,
574 dict_get_split_vars (default_dict),
575 dict_get_split_cnt (default_dict)) == 0;
578 /* Multipass procedure that separates the data into SPLIT FILE
581 /* Represents auxiliary data for handling SPLIT FILE in a
582 multipass procedure. */
583 struct multipass_split_aux_data
585 struct ccase prev_case; /* Data in previous case. */
586 struct casefile *casefile; /* Accumulates data for a split. */
588 /* Function to call with the accumulated data. */
589 bool (*split_func) (const struct ccase *first, const struct casefile *,
591 void *func_aux; /* Auxiliary data. */
594 static bool multipass_split_case_func (const struct ccase *c, void *aux_);
595 static bool multipass_split_end_func (void *aux_);
596 static bool multipass_split_output (struct multipass_split_aux_data *);
598 /* Returns true if successful, false if an I/O error occurred. */
600 multipass_procedure_with_splits (bool (*split_func) (const struct ccase *first,
601 const struct casefile *,
605 struct multipass_split_aux_data aux;
608 case_nullify (&aux.prev_case);
610 aux.split_func = split_func;
611 aux.func_aux = func_aux;
613 ok = internal_procedure (multipass_split_case_func,
614 multipass_split_end_func, &aux);
615 case_destroy (&aux.prev_case);
620 /* Case callback used by multipass_procedure_with_splits(). */
622 multipass_split_case_func (const struct ccase *c, void *aux_)
624 struct multipass_split_aux_data *aux = aux_;
627 /* Start a new series if needed. */
628 if (aux->casefile == NULL || !equal_splits (c, &aux->prev_case))
630 /* Record split values. */
631 case_destroy (&aux->prev_case);
632 case_clone (&aux->prev_case, c);
634 /* Pass any cases to split_func. */
635 if (aux->casefile != NULL)
636 ok = multipass_split_output (aux);
638 /* Start a new casefile. */
639 aux->casefile = casefile_create (dict_get_next_value_idx (default_dict));
642 return casefile_append (aux->casefile, c) && ok;
645 /* End-of-file callback used by multipass_procedure_with_splits(). */
647 multipass_split_end_func (void *aux_)
649 struct multipass_split_aux_data *aux = aux_;
650 return (aux->casefile == NULL || multipass_split_output (aux));
654 multipass_split_output (struct multipass_split_aux_data *aux)
658 assert (aux->casefile != NULL);
659 ok = aux->split_func (&aux->prev_case, aux->casefile, aux->func_aux);
660 casefile_destroy (aux->casefile);
661 aux->casefile = NULL;
666 /* Discards all the current state in preparation for a data-input
667 command like DATA LIST or GET. */
669 discard_variables (void)
671 dict_clear (default_dict);
672 fh_set_default_handle (NULL);
676 free_case_source (vfm_source);
679 proc_cancel_all_transformations ();
681 expr_free (process_if_expr);
682 process_if_expr = NULL;
684 proc_cancel_temporary_transformations ();
687 /* Returns the current set of permanent transformations,
688 and clears the permanent transformations.
689 For use by INPUT PROGRAM. */
691 proc_capture_transformations (void)
693 struct trns_chain *chain;
695 assert (temporary_trns_chain == NULL);
696 chain = permanent_trns_chain;
697 cur_trns_chain = permanent_trns_chain = trns_chain_create ();
701 /* Adds a transformation that processes a case with PROC and
702 frees itself with FREE to the current set of transformations.
703 The functions are passed AUX as auxiliary data. */
705 add_transformation (trns_proc_func *proc, trns_free_func *free, void *aux)
707 trns_chain_append (cur_trns_chain, NULL, proc, free, aux);
710 /* Adds a transformation that processes a case with PROC and
711 frees itself with FREE to the current set of transformations.
712 When parsing of the block of transformations is complete,
713 FINALIZE will be called.
714 The functions are passed AUX as auxiliary data. */
716 add_transformation_with_finalizer (trns_finalize_func *finalize,
717 trns_proc_func *proc,
718 trns_free_func *free, void *aux)
720 trns_chain_append (cur_trns_chain, finalize, proc, free, aux);
723 /* Returns the index of the next transformation.
724 This value can be returned by a transformation procedure
725 function to indicate a "jump" to that transformation. */
727 next_transformation (void)
729 return trns_chain_next (cur_trns_chain);
732 /* Returns true if the next call to add_transformation() will add
733 a temporary transformation, false if it will add a permanent
736 proc_in_temporary_transformations (void)
738 return temporary_trns_chain != NULL;
741 /* Marks the start of temporary transformations.
742 Further calls to add_transformation() will add temporary
745 proc_start_temporary_transformations (void)
747 if (!proc_in_temporary_transformations ())
749 add_case_limit_trns ();
751 permanent_dict = dict_clone (default_dict);
752 trns_chain_finalize (permanent_trns_chain);
753 temporary_trns_chain = cur_trns_chain = trns_chain_create ();
757 /* Converts all the temporary transformations, if any, to
758 permanent transformations. Further transformations will be
760 Returns true if anything changed, false otherwise. */
762 proc_make_temporary_transformations_permanent (void)
764 if (proc_in_temporary_transformations ())
766 trns_chain_finalize (temporary_trns_chain);
767 trns_chain_splice (permanent_trns_chain, temporary_trns_chain);
768 temporary_trns_chain = NULL;
770 dict_destroy (permanent_dict);
771 permanent_dict = NULL;
779 /* Cancels all temporary transformations, if any. Further
780 transformations will be permanent.
781 Returns true if anything changed, false otherwise. */
783 proc_cancel_temporary_transformations (void)
785 if (proc_in_temporary_transformations ())
787 dict_destroy (default_dict);
788 default_dict = permanent_dict;
789 permanent_dict = NULL;
791 trns_chain_destroy (temporary_trns_chain);
792 temporary_trns_chain = NULL;
800 /* Cancels all transformations, if any.
801 Returns true if successful, false on I/O error. */
803 proc_cancel_all_transformations (void)
806 ok = trns_chain_destroy (permanent_trns_chain);
807 ok = trns_chain_destroy (temporary_trns_chain) && ok;
808 permanent_trns_chain = cur_trns_chain = trns_chain_create ();
809 temporary_trns_chain = NULL;
813 /* Initializes procedure handling. */
817 default_dict = dict_create ();
818 proc_cancel_all_transformations ();
821 /* Finishes up procedure handling. */
825 discard_variables ();
828 /* Sets SINK as the destination for procedure output from the
831 proc_set_sink (struct case_sink *sink)
833 assert (vfm_sink == NULL);
837 /* Sets SOURCE as the source for procedure input for the next
840 proc_set_source (struct case_source *source)
842 assert (vfm_source == NULL);
846 /* Returns true if a source for the next procedure has been
847 configured, false otherwise. */
849 proc_has_source (void)
851 return vfm_source != NULL;
854 /* Returns the output from the previous procedure.
855 For use only immediately after executing a procedure.
856 The returned casefile is owned by the caller; it will not be
857 automatically used for the next procedure's input. */
859 proc_capture_output (void)
861 struct casefile *casefile;
863 /* Try to make sure that this function is called immediately
864 after procedure() or a similar function. */
865 assert (vfm_source != NULL);
866 assert (case_source_is_class (vfm_source, &storage_source_class));
867 assert (trns_chain_is_empty (permanent_trns_chain));
868 assert (!proc_in_temporary_transformations ());
870 casefile = storage_source_decapsulate (vfm_source);
876 static trns_proc_func case_limit_trns_proc;
877 static trns_free_func case_limit_trns_free;
879 /* Adds a transformation that limits the number of cases that may
880 pass through, if default_dict has a case limit. */
882 add_case_limit_trns (void)
884 size_t case_limit = dict_get_case_limit (default_dict);
887 size_t *cases_remaining = xmalloc (sizeof *cases_remaining);
888 *cases_remaining = case_limit;
889 add_transformation (case_limit_trns_proc, case_limit_trns_free,
891 dict_set_case_limit (default_dict, 0);
895 /* Limits the maximum number of cases processed to
898 case_limit_trns_proc (void *cases_remaining_,
899 struct ccase *c UNUSED, int case_nr UNUSED)
901 size_t *cases_remaining = cases_remaining_;
902 if (*cases_remaining > 0)
905 return TRNS_CONTINUE;
908 return TRNS_DROP_CASE;
911 /* Frees the data associated with a case limit transformation. */
913 case_limit_trns_free (void *cases_remaining_)
915 size_t *cases_remaining = cases_remaining_;
916 free (cases_remaining);
920 static trns_proc_func filter_trns_proc;
922 /* Adds a temporary transformation to filter data according to
923 the variable specified on FILTER, if any. */
925 add_filter_trns (void)
927 struct variable *filter_var = dict_get_filter (default_dict);
928 if (filter_var != NULL)
930 proc_start_temporary_transformations ();
931 add_transformation (filter_trns_proc, NULL, filter_var);
935 /* FILTER transformation. */
937 filter_trns_proc (void *filter_var_,
938 struct ccase *c UNUSED, int case_nr UNUSED)
941 struct variable *filter_var = filter_var_;
942 double f = case_num (c, filter_var->fv);
943 return (f != 0.0 && !mv_is_num_missing (&filter_var->miss, f)
944 ? TRNS_CONTINUE : TRNS_DROP_CASE);
947 static trns_proc_func process_if_trns_proc;
948 static trns_free_func process_if_trns_free;
950 /* Adds a temporary transformation to filter data according to
951 the expression specified on PROCESS IF, if any. */
953 add_process_if_trns (void)
955 if (process_if_expr != NULL)
957 proc_start_temporary_transformations ();
958 add_transformation (process_if_trns_proc, process_if_trns_free,
960 process_if_expr = NULL;
964 /* PROCESS IF transformation. */
966 process_if_trns_proc (void *expression_,
967 struct ccase *c UNUSED, int case_nr UNUSED)
970 struct expression *expression = expression_;
971 return (expr_evaluate_num (expression, c, case_nr) == 1.0
972 ? TRNS_CONTINUE : TRNS_DROP_CASE);
975 /* Frees a PROCESS IF transformation. */
977 process_if_trns_free (void *expression_)
979 struct expression *expression = expression_;
980 expr_free (expression);