1 /* PSPP - computes sample statistics.
2 Copyright (C) 1997-9, 2000 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., 59 Temple Place - Suite 330, Boston, MA
20 /* AIX requires this to be the first thing in the file. */
23 #define alloca __builtin_alloca
31 #ifndef alloca /* predefined by HP cc +Olibcalls */
45 #include <unistd.h> /* Required by SunOS4. */
58 #include "value-labels.h"
61 Virtual File Manager (vfm):
63 vfm is used to process data files. It uses the model that data is
64 read from one stream (the data source), then written to another
65 (the data sink). The data source is then deleted and the data sink
66 becomes the data source for the next procedure. */
68 #include "debug-print.h"
70 /* This is used to read from the active file. */
71 struct case_stream *vfm_source;
73 /* `value' indexes to initialize to particular values for certain cases. */
74 struct long_vec reinit_sysmis; /* SYSMIS for every case. */
75 struct long_vec reinit_blanks; /* Blanks for every case. */
76 struct long_vec init_zero; /* Zero for first case only. */
77 struct long_vec init_blanks; /* Blanks for first case only. */
79 /* This is used to write to the replacement active file. */
80 struct case_stream *vfm_sink;
82 /* Information about the data source. */
83 struct stream_info vfm_source_info;
85 /* Information about the data sink. */
86 struct stream_info vfm_sink_info;
88 /* Filter variable and `value' index. */
89 static struct variable *filter_var;
90 static int filter_index;
94 && (temp_case->data[filter_index].f == 0.0 \
95 || temp_case->data[filter_index].f == SYSMIS \
96 || is_num_user_missing (temp_case->data[filter_index].f, \
99 /* Nonzero if the case needs to have values deleted before being
100 stored, zero otherwise. */
101 int compaction_necessary;
103 /* Number of values after compaction, or the same as
104 vfm_sink_info.nval, if compaction is not necessary. */
107 /* Temporary case buffer with enough room for `compaction_nval'
109 struct ccase *compaction_case;
111 /* Within a session, when paging is turned on, it is never turned back
112 off. This policy might be too aggressive. */
113 static int paging = 0;
115 /* Time at which vfm was last invoked. */
116 time_t last_vfm_invocation;
118 /* Functions called during procedure processing. */
119 static int (*proc_func) (struct ccase *); /* Called for each case. */
120 static int (*virt_proc_func) (struct ccase *); /* From SPLIT_FILE_procfunc. */
121 static void (*begin_func) (void); /* Called at beginning of a series. */
122 static void (*virt_begin_func) (void); /* Called by SPLIT_FILE_procfunc. */
123 static void (*end_func) (void); /* Called after end of a series. */
124 int (*write_case) (void);
126 /* Number of cases passed to proc_func(). */
127 static int case_count;
130 int n_lag; /* Number of cases to lag. */
131 static int lag_count; /* Number of cases in lag_queue so far. */
132 static int lag_head; /* Index where next case will be added. */
133 static struct ccase **lag_queue; /* Array of n_lag ccase * elements. */
135 static void open_active_file (void);
136 static void close_active_file (void);
137 static int SPLIT_FILE_procfunc (struct ccase *);
138 static void finish_compaction (void);
139 static void lag_case (void);
140 static int procedure_write_case (void);
142 /* Public functions. */
144 /* Reads all the cases from the active file, transforms them by the
145 active set of transformations, calls PROCFUNC with CURCASE set to
146 the case and CASENUM set to the case number, and writes them to a
149 Divides the active file into zero or more series of one or more
150 cases each. BEGINFUNC is called before each series. ENDFUNC is
151 called after each series. */
153 procedure (void (*beginfunc) (void),
154 int (*procfunc) (struct ccase *curcase),
155 void (*endfunc) (void))
158 write_case = procedure_write_case;
160 if (dict_get_split_cnt (default_dict) != 0 && procfunc != NULL)
162 virt_proc_func = procfunc;
163 proc_func = SPLIT_FILE_procfunc;
165 virt_begin_func = beginfunc;
168 begin_func = beginfunc;
169 proc_func = procfunc;
172 last_vfm_invocation = time (NULL);
176 close_active_file ();
179 /* Active file processing support. Subtly different semantics from
182 static int process_active_file_write_case (void);
184 /* The casefunc might want us to stop calling it. */
185 static int not_canceled;
187 /* Reads all the cases from the active file and passes them one-by-one
188 to CASEFUNC in temp_case. Before any cases are passed, calls
189 BEGINFUNC. After all the cases have been passed, calls ENDFUNC.
190 BEGINFUNC, CASEFUNC, and ENDFUNC can write temp_case to the output
191 file by calling process_active_file_output_case().
193 process_active_file() ignores TEMPORARY, SPLIT FILE, and N. */
195 process_active_file (void (*beginfunc) (void),
196 int (*casefunc) (struct ccase *curcase),
197 void (*endfunc) (void))
199 proc_func = casefunc;
200 write_case = process_active_file_write_case;
206 /* There doesn't necessarily need to be an active file. */
211 close_active_file ();
214 /* Pass the current case to casefunc. */
216 process_active_file_write_case (void)
218 /* Index of current transformation. */
221 for (cur_trns = f_trns ; cur_trns != temp_trns; )
225 code = t_trns[cur_trns]->proc (t_trns[cur_trns], temp_case);
229 /* Next transformation. */
233 /* Delete this case. */
236 /* Go to that transformation. */
245 /* Call the procedure if FILTER and PROCESS IF don't prohibit it. */
248 && (process_if_expr == NULL ||
249 expr_evaluate (process_if_expr, temp_case, NULL) == 1.0))
250 not_canceled = proc_func (temp_case);
258 /* This case is finished. Initialize the variables for the next case. */
259 for (lp = reinit_sysmis.vec; *lp != -1;)
260 temp_case->data[*lp++].f = SYSMIS;
261 for (lp = reinit_blanks.vec; *lp != -1;)
262 memset (temp_case->data[*lp++].s, ' ', MAX_SHORT_STRING);
268 /* Write temp_case to the active file. */
270 process_active_file_output_case (void)
272 vfm_sink_info.ncases++;
276 /* Opening the active file. */
278 /* It might be usefully noted that the following several functions are
279 given in the order that they are called by open_active_file(). */
281 /* Prepare to write to the replacement active file. */
283 prepare_for_writing (void)
285 /* FIXME: If ALL the conditions listed below hold true, then the
286 replacement active file is guaranteed to be identical to the
287 original active file:
289 1. TEMPORARY was the first transformation, OR, there were no
290 transformations at all.
292 2. Input is not coming from an input program.
294 3. Compaction is not necessary.
296 So, in this case, we shouldn't have to replace the active
297 file--it's just a waste of time and space. */
299 vfm_sink_info.ncases = 0;
300 vfm_sink_info.nval = dict_get_value_cnt (default_dict);
301 vfm_sink_info.case_size = (sizeof (struct ccase)
302 + ((dict_get_value_cnt (default_dict) - 1)
303 * sizeof (union value)));
305 if (vfm_sink == NULL)
307 if (vfm_sink_info.case_size * vfm_source_info.ncases > MAX_WORKSPACE
310 msg (MW, _("Workspace overflow predicted. Max workspace is "
311 "currently set to %d KB (%d cases at %d bytes each). "
312 "Paging active file to disk."),
313 MAX_WORKSPACE / 1024, MAX_WORKSPACE / vfm_sink_info.case_size,
314 vfm_sink_info.case_size);
319 vfm_sink = paging ? &vfm_disk_stream : &vfm_memory_stream;
323 /* Arrange for compacting the output cases for storage. */
325 arrange_compaction (void)
327 int count_values = 0;
332 /* Count up the number of `value's that will be output. */
333 for (i = 0; i < dict_get_var_cnt (temp_dict); i++)
335 struct variable *v = dict_get_var (temp_dict, i);
337 if (v->name[0] != '#')
340 count_values += v->nv;
343 assert (temporary == 2 || count_values <= dict_get_value_cnt (temp_dict));
346 /* Compaction is only necessary if the number of `value's to output
347 differs from the number already present. */
348 compaction_nval = count_values;
349 compaction_necessary = (temporary == 2
350 || count_values != dict_get_value_cnt (temp_dict));
356 /* Prepares the temporary case and compaction case. */
358 make_temp_case (void)
360 temp_case = xmalloc (vfm_sink_info.case_size);
362 if (compaction_necessary)
363 compaction_case = xmalloc (sizeof (struct ccase)
364 + sizeof (union value) * (compaction_nval - 1));
368 /* Returns the name of the variable that owns the index CCASE_INDEX
371 index_to_varname (int ccase_index)
375 for (i = 0; i < default_dict.nvar; i++)
377 struct variable *v = default_dict.var[i];
379 if (ccase_index >= v->fv && ccase_index < v->fv + v->nv)
380 return default_dict.var[i]->name;
386 /* Initializes temp_case from the vectors that say which `value's need
387 to be initialized just once, and which ones need to be
388 re-initialized before every case. */
390 vector_initialization (void)
396 for (i = 0; i < init_zero.n; i++)
397 temp_case->data[init_zero.vec[i]].f = 0.0;
398 for (i = 0; i < init_blanks.n; i++)
399 memset (temp_case->data[init_blanks.vec[i]].s, ' ', MAX_SHORT_STRING);
401 /* These vectors need to be repeatedly accessed, so we add a
402 sentinel to (hopefully) improve speed. */
403 vec_insert (&reinit_sysmis, -1);
404 vec_insert (&reinit_blanks, -1);
406 for (lp = reinit_sysmis.vec; *lp != -1;)
407 temp_case->data[*lp++].f = SYSMIS;
408 for (lp = reinit_blanks.vec; *lp != -1;)
409 memset (temp_case->data[*lp++].s, ' ', MAX_SHORT_STRING);
412 printf ("vfm: init_zero=");
413 for (i = 0; i < init_zero.n; i++)
414 printf ("%s%s", i ? "," : "", index_to_varname (init_zero.vec[i]));
415 printf (" init_blanks=");
416 for (i = 0; i < init_blanks.n; i++)
417 printf ("%s%s", i ? "," : "", index_to_varname (init_blanks.vec[i]));
418 printf (" reinit_sysmis=");
419 for (lp = reinit_sysmis.vec; *lp != -1; lp++)
420 printf ("%s%s", lp != reinit_sysmis.vec ? "," : "",
421 index_to_varname (*lp));
422 printf (" reinit_blanks=");
423 for (lp = reinit_blanks.vec; *lp != -1; lp++)
424 printf ("%s%s", lp != reinit_blanks.vec ? "," : "",
425 index_to_varname (*lp));
430 /* Sets filter_index to an appropriate value. */
434 filter_var = dict_get_filter (default_dict);
436 if (filter_var != NULL)
438 assert (filter_var->type == NUMERIC);
439 filter_index = filter_var->index;
445 /* Sets all the lag-related variables based on value of n_lag. */
456 lag_queue = xmalloc (n_lag * sizeof *lag_queue);
457 for (i = 0; i < n_lag; i++)
458 lag_queue[i] = xmalloc (dict_get_value_cnt (temp_dict)
459 * sizeof **lag_queue);
462 /* There is a lot of potential confusion in the vfm and related
463 routines over the number of `value's at each stage of the process.
464 Here is each nval count, with explanation, as set up by
467 vfm_source_info.nval: Number of `value's in the cases returned by
468 the source stream. This value turns out not to be very useful, but
469 we maintain it anyway.
471 vfm_sink_info.nval: Number of `value's in the cases after all
472 transformations have been performed. Never less than
473 vfm_source_info.nval.
475 temp_dict->nval: Number of `value's in the cases after the
476 transformations leading up to TEMPORARY have been performed. If
477 TEMPORARY was not specified, this is equal to vfm_sink_info.nval.
478 Never less than vfm_sink_info.nval.
480 compaction_nval: Number of `value's in the cases after the
481 transformations leading up to TEMPORARY have been performed and the
482 case has been compacted by compact_case(), if compaction is
483 necessary. This the number of `value's in the cases saved by the
484 sink stream. (However, note that the cases passed to the sink
485 stream have not yet been compacted. It is the responsibility of
486 the data sink to call compact_case().) This may be less than,
487 greater than, or equal to vfm_source_info.nval. `compaction'
488 becomes the new value of default_dict.nval after the procedure is
491 default_dict.nval: This is often an alias for temp_dict->nval. As
492 such it can really have no separate existence until the procedure
493 is complete. For this reason it should *not* be referenced inside
494 the execution of a procedure. */
495 /* Makes all preparations for reading from the data source and writing
498 open_active_file (void)
500 /* Sometimes we want to refer to the dictionary that applies to the
501 data actually written to the sink. This is either temp_dict or
502 default_dict. However, if TEMPORARY is not on, then temp_dict
503 does not apply. So, we can set temp_dict to default_dict in this
508 temp_dict = default_dict;
511 /* No cases passed to the procedure yet. */
515 prepare_for_writing ();
516 arrange_compaction ();
518 vector_initialization ();
519 discard_ctl_stack ();
524 debug_printf (("vfm: reading from %s source, writing to %s sink.\n",
525 vfm_source->name, vfm_sink->name));
526 debug_printf (("vfm: vfm_source_info.nval=%d, vfm_sink_info.nval=%d, "
527 "temp_dict->nval=%d, compaction_nval=%d, "
528 "default_dict.nval=%d\n",
529 vfm_source_info.nval, vfm_sink_info.nval, temp_dict->nval,
530 compaction_nval, default_dict.nval));
533 /* Closes the active file. */
535 close_active_file (void)
537 /* Close the current case group. */
538 if (case_count && end_func != NULL)
541 /* Stop lagging (catch up?). */
546 for (i = 0; i < n_lag; i++)
552 /* Assume the dictionary from right before TEMPORARY, if any. Turn
556 dict_destroy (default_dict);
557 default_dict = temp_dict;
561 /* Finish compaction. */
562 if (compaction_necessary)
563 finish_compaction ();
565 /* Old data sink --> New data source. */
566 if (vfm_source && vfm_source->destroy_source)
567 vfm_source->destroy_source ();
569 vfm_source = vfm_sink;
570 vfm_source_info.ncases = vfm_sink_info.ncases;
571 vfm_source_info.nval = compaction_nval;
572 vfm_source_info.case_size = (sizeof (struct ccase)
573 + (compaction_nval - 1) * sizeof (union value));
574 if (vfm_source->mode)
577 /* Old data sink is gone now. */
580 /* Cancel TEMPORARY. */
583 /* Free temporary cases. */
587 free (compaction_case);
588 compaction_case = NULL;
590 /* Cancel PROCESS IF. */
591 expr_free (process_if_expr);
592 process_if_expr = NULL;
594 /* Cancel FILTER if temporary. */
595 if (filter_var != NULL && !FILTER_before_TEMPORARY)
596 dict_set_filter (default_dict, NULL);
598 /* Cancel transformations. */
599 cancel_transformations ();
601 /* Clear value-initialization vectors. */
602 vec_clear (&init_zero);
603 vec_clear (&init_blanks);
604 vec_clear (&reinit_sysmis);
605 vec_clear (&reinit_blanks);
607 /* Turn off case limiter. */
608 dict_set_case_limit (default_dict, 0);
610 /* Clear VECTOR vectors. */
611 dict_clear_vectors (default_dict);
613 debug_printf (("vfm: procedure complete\n\n"));
616 /* Disk case stream. */
618 /* Associated files. */
619 FILE *disk_source_file;
620 FILE *disk_sink_file;
622 /* Initializes the disk sink. */
624 disk_stream_init (void)
626 disk_sink_file = tmpfile ();
629 msg (ME, _("An error occurred attempting to create a temporary "
630 "file for use as the active file: %s."),
636 /* Reads all cases from the disk source and passes them one by one to
639 disk_stream_read (void)
643 for (i = 0; i < vfm_source_info.ncases; i++)
645 if (!fread (temp_case, vfm_source_info.case_size, 1, disk_source_file))
647 msg (ME, _("An error occurred while attempting to read from "
648 "a temporary file created for the active file: %s."),
659 /* Writes temp_case to the disk sink. */
661 disk_stream_write (void)
663 union value *src_case;
665 if (compaction_necessary)
667 compact_case (compaction_case, temp_case);
668 src_case = (union value *) compaction_case;
670 else src_case = (union value *) temp_case;
672 if (fwrite (src_case, sizeof *src_case * compaction_nval, 1,
673 disk_sink_file) != 1)
675 msg (ME, _("An error occurred while attempting to write to a "
676 "temporary file used as the active file: %s."),
682 /* Switches the stream from a sink to a source. */
684 disk_stream_mode (void)
686 /* Rewind the sink. */
687 if (fseek (disk_sink_file, 0, SEEK_SET) != 0)
689 msg (ME, _("An error occurred while attempting to rewind a "
690 "temporary file used as the active file: %s."),
695 /* Sink --> source variables. */
696 disk_source_file = disk_sink_file;
699 /* Destroys the source's internal data. */
701 disk_stream_destroy_source (void)
703 if (disk_source_file)
705 fclose (disk_source_file);
706 disk_source_file = NULL;
710 /* Destroys the sink's internal data. */
712 disk_stream_destroy_sink (void)
716 fclose (disk_sink_file);
717 disk_sink_file = NULL;
722 struct case_stream vfm_disk_stream =
728 disk_stream_destroy_source,
729 disk_stream_destroy_sink,
733 /* Memory case stream. */
735 /* List of cases stored in the stream. */
736 struct case_list *memory_source_cases;
737 struct case_list *memory_sink_cases;
740 struct case_list *memory_sink_iter;
742 /* Maximum number of cases. */
743 int memory_sink_max_cases;
745 /* Initializes the memory stream variables for writing. */
747 memory_stream_init (void)
749 memory_sink_cases = NULL;
750 memory_sink_iter = NULL;
752 assert (compaction_nval);
753 memory_sink_max_cases = MAX_WORKSPACE / (sizeof (union value) * compaction_nval);
756 /* Reads the case stream from memory and passes it to write_case(). */
758 memory_stream_read (void)
760 while (memory_source_cases != NULL)
762 memcpy (temp_case, &memory_source_cases->c, vfm_source_info.case_size);
765 struct case_list *current = memory_source_cases;
766 memory_source_cases = memory_source_cases->next;
775 /* Writes temp_case to the memory stream. */
777 memory_stream_write (void)
779 struct case_list *new_case = malloc (sizeof (struct case_list)
780 + ((compaction_nval - 1)
781 * sizeof (union value)));
783 /* If we've got memory to spare then add it to the linked list. */
784 if (vfm_sink_info.ncases <= memory_sink_max_cases && new_case != NULL)
786 if (compaction_necessary)
787 compact_case (&new_case->c, temp_case);
789 memcpy (&new_case->c, temp_case, sizeof (union value) * compaction_nval);
791 /* Append case to linked list. */
792 if (memory_sink_cases)
793 memory_sink_iter = memory_sink_iter->next = new_case;
795 memory_sink_iter = memory_sink_cases = new_case;
799 /* Out of memory. Write the active file to disk. */
800 struct case_list *cur, *next;
802 /* Notify the user. */
804 msg (MW, _("Virtual memory exhausted. Paging active file "
807 msg (MW, _("Workspace limit of %d KB (%d cases at %d bytes each) "
808 "overflowed. Paging active file to disk."),
809 MAX_WORKSPACE / 1024, memory_sink_max_cases,
810 compaction_nval * sizeof (union value));
814 /* Switch to a disk sink. */
815 vfm_sink = &vfm_disk_stream;
819 /* Terminate the list. */
820 if (memory_sink_iter)
821 memory_sink_iter->next = NULL;
823 /* Write the cases to disk and destroy them. We can't call
824 vfm->sink->write() because of compaction. */
825 for (cur = memory_sink_cases; cur; cur = next)
828 if (fwrite (cur->c.data, sizeof (union value) * compaction_nval, 1,
829 disk_sink_file) != 1)
831 msg (ME, _("An error occurred while attempting to "
832 "write to a temporary file created as the "
833 "active file, while paging to disk: %s."),
840 /* Write the current case to disk. */
845 /* If the data is stored in memory, causes it to be written to disk.
846 To be called only *between* procedure()s, not within them. */
850 if (vfm_source == &vfm_memory_stream)
852 /* Switch to a disk sink. */
853 vfm_sink = &vfm_disk_stream;
857 /* Write the cases to disk and destroy them. We can't call
858 vfm->sink->write() because of compaction. */
860 struct case_list *cur, *next;
862 for (cur = memory_source_cases; cur; cur = next)
865 if (fwrite (cur->c.data, sizeof *cur->c.data * compaction_nval, 1,
866 disk_sink_file) != 1)
868 msg (ME, _("An error occurred while attempting to "
869 "write to a temporary file created as the "
870 "active file, while paging to disk: %s."),
878 vfm_source = &vfm_disk_stream;
885 /* Switch the memory stream from sink to source mode. */
887 memory_stream_mode (void)
889 /* Terminate the list. */
890 if (memory_sink_iter)
891 memory_sink_iter->next = NULL;
893 /* Sink --> source variables. */
894 memory_source_cases = memory_sink_cases;
895 memory_sink_cases = NULL;
898 /* Destroy all memory source data. */
900 memory_stream_destroy_source (void)
902 struct case_list *cur, *next;
904 for (cur = memory_source_cases; cur; cur = next)
909 memory_source_cases = NULL;
912 /* Destroy all memory sink data. */
914 memory_stream_destroy_sink (void)
916 struct case_list *cur, *next;
918 for (cur = memory_sink_cases; cur; cur = next)
923 memory_sink_cases = NULL;
927 struct case_stream vfm_memory_stream =
933 memory_stream_destroy_source,
934 memory_stream_destroy_sink,
938 #include "debug-print.h"
940 /* Add temp_case to the lag queue. */
944 if (lag_count < n_lag)
946 memcpy (lag_queue[lag_head], temp_case,
947 sizeof (union value) * dict_get_value_cnt (temp_dict));
948 if (++lag_head >= n_lag)
952 /* Returns a pointer to the lagged case from N_BEFORE cases before the
953 current one, or NULL if there haven't been that many cases yet. */
955 lagged_case (int n_before)
957 assert (n_before <= n_lag);
958 if (n_before > lag_count)
962 int index = lag_head - n_before;
965 return lag_queue[index];
969 /* Transforms temp_case and writes it to the replacement active file
970 if advisable. Returns nonzero if more cases can be accepted, zero
971 otherwise. Do not call this function again after it has returned
974 procedure_write_case (void)
976 /* Index of current transformation. */
979 /* Return value: whether it's reasonable to write any more cases. */
982 debug_printf ((_("transform: ")));
987 /* Output the case if this is temp_trns. */
988 if (cur_trns == temp_trns)
990 debug_printf (("REC"));
995 vfm_sink_info.ncases++;
998 if (dict_get_case_limit (default_dict))
999 more_cases = (vfm_sink_info.ncases
1000 < dict_get_case_limit (default_dict));
1004 if (cur_trns >= n_trns)
1007 debug_printf (("$%d", cur_trns));
1009 /* Decide which transformation should come next. */
1013 code = t_trns[cur_trns]->proc (t_trns[cur_trns], temp_case);
1017 /* Next transformation. */
1021 /* Delete this case. */
1024 /* Go to that transformation. */
1031 /* Call the beginning of group function. */
1032 if (!case_count && begin_func != NULL)
1035 /* Call the procedure if there is one and FILTER and PROCESS IF
1036 don't prohibit it. */
1037 if (proc_func != NULL
1039 && (process_if_expr == NULL ||
1040 expr_evaluate (process_if_expr, temp_case, NULL) == 1.0))
1041 proc_func (temp_case);
1046 debug_putc ('\n', stdout);
1051 /* This case is finished. Initialize the variables for the next case. */
1052 for (lp = reinit_sysmis.vec; *lp != -1;)
1053 temp_case->data[*lp++].f = SYSMIS;
1054 for (lp = reinit_blanks.vec; *lp != -1;)
1055 memset (temp_case->data[*lp++].s, ' ', MAX_SHORT_STRING);
1058 /* Return previously determined value. */
1062 /* Appends TRNS to t_trns[], the list of all transformations to be
1063 performed on data as it is read from the active file. */
1065 add_transformation (struct trns_header * trns)
1067 if (n_trns >= m_trns)
1070 t_trns = xrealloc (t_trns, sizeof *t_trns * m_trns);
1072 t_trns[n_trns] = trns;
1073 trns->index = n_trns++;
1076 /* Cancels all active transformations, including any transformations
1077 created by the input program. */
1079 cancel_transformations (void)
1082 for (i = 0; i < n_trns; i++)
1084 if (t_trns[i]->free)
1085 t_trns[i]->free (t_trns[i]);
1088 n_trns = f_trns = 0;
1096 /* Dumps out the values of all the split variables for the case C. */
1098 dump_splits (struct ccase *c)
1100 struct variable *const *split;
1101 struct tab_table *t;
1105 split_cnt = dict_get_split_cnt (default_dict);
1106 t = tab_create (3, split_cnt + 1, 0);
1107 tab_dim (t, tab_natural_dimensions);
1108 tab_vline (t, TAL_1 | TAL_SPACING, 1, 0, split_cnt);
1109 tab_vline (t, TAL_1 | TAL_SPACING, 2, 0, split_cnt);
1110 tab_text (t, 0, 0, TAB_NONE, _("Variable"));
1111 tab_text (t, 1, 0, TAB_LEFT, _("Value"));
1112 tab_text (t, 2, 0, TAB_LEFT, _("Label"));
1113 split = dict_get_split_vars (default_dict);
1114 for (i = 0; i < split_cnt; i++)
1116 struct variable *v = split[i];
1118 const char *val_lab;
1120 assert (v->type == NUMERIC || v->type == ALPHA);
1121 tab_text (t, 0, i + 1, TAB_LEFT | TAT_PRINTF, "%s", v->name);
1124 union value val = c->data[v->fv];
1125 if (v->type == ALPHA)
1126 val.c = c->data[v->fv].s;
1127 data_out (temp_buf, &v->print, &val);
1130 temp_buf[v->print.w] = 0;
1131 tab_text (t, 1, i + 1, TAT_PRINTF, "%.*s", v->print.w, temp_buf);
1133 val_lab = val_labs_find (v->val_labs, c->data[v->fv]);
1135 tab_text (t, 2, i + 1, TAB_LEFT, val_lab);
1137 tab_flags (t, SOMF_NO_TITLE);
1141 /* This procfunc is substituted for the user-supplied procfunc when
1142 SPLIT FILE is active. This function forms a wrapper around that
1143 procfunc by dividing the input into series. */
1145 SPLIT_FILE_procfunc (struct ccase *c)
1147 static struct ccase *prev_case;
1148 struct variable *const *split;
1152 /* The first case always begins a new series. We also need to
1153 preserve the values of the case for later comparison. */
1154 if (case_count == 0)
1158 prev_case = xmalloc (vfm_sink_info.case_size);
1159 memcpy (prev_case, c, vfm_sink_info.case_size);
1162 if (virt_begin_func != NULL)
1165 return virt_proc_func (c);
1168 /* Compare the value of each SPLIT FILE variable to the values on
1169 the previous case. */
1170 split = dict_get_split_vars (default_dict);
1171 split_cnt = dict_get_split_cnt (default_dict);
1172 for (i = 0; i < split_cnt; i++)
1174 struct variable *v = split[i];
1179 if (approx_ne (c->data[v->fv].f, prev_case->data[v->fv].f))
1183 if (memcmp (c->data[v->fv].s, prev_case->data[v->fv].s, v->width))
1190 return virt_proc_func (c);
1193 /* The values of the SPLIT FILE variable are different from the
1194 values on the previous case. That means that it's time to begin
1196 if (end_func != NULL)
1199 if (virt_begin_func != NULL)
1201 memcpy (prev_case, c, vfm_sink_info.case_size);
1202 return virt_proc_func (c);
1205 /* Case compaction. */
1207 /* Copies case SRC to case DEST, compacting it in the process. */
1209 compact_case (struct ccase *dest, const struct ccase *src)
1215 assert (compaction_necessary);
1219 if (dest != compaction_case)
1220 memcpy (dest, compaction_case, sizeof (union value) * compaction_nval);
1224 /* Copy all the variables except the scratch variables from SRC to
1226 var_cnt = dict_get_var_cnt (default_dict);
1227 for (i = 0; i < var_cnt; i++)
1229 struct variable *v = dict_get_var (default_dict, i);
1231 if (v->name[0] == '#')
1234 if (v->type == NUMERIC)
1235 dest->data[nval++] = src->data[v->fv];
1238 int w = DIV_RND_UP (v->width, sizeof (union value));
1240 memcpy (&dest->data[nval], &src->data[v->fv], w * sizeof (union value));
1246 /* Reassigns `fv' for each variable. Deletes scratch variables. */
1248 finish_compaction (void)
1252 for (i = 0; i < dict_get_var_cnt (default_dict); )
1254 struct variable *v = dict_get_var (default_dict, i);
1256 if (v->name[0] == '#')
1257 dict_delete_var (default_dict, v);
1261 dict_compact_values (default_dict);