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 */
43 #include <unistd.h> /* Required by SunOS4. */
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. */
69 /*#define DEBUGGING 1 */
70 #include "debug-print.h"
72 /* This is used to read from the active file. */
73 struct case_stream *vfm_source;
75 /* `value' indexes to initialize to particular values for certain cases. */
76 struct long_vec reinit_sysmis; /* SYSMIS for every case. */
77 struct long_vec reinit_blanks; /* Blanks for every case. */
78 struct long_vec init_zero; /* Zero for first case only. */
79 struct long_vec init_blanks; /* Blanks for first case only. */
81 /* This is used to write to the replacement active file. */
82 struct case_stream *vfm_sink;
84 /* Information about the data source. */
85 struct stream_info vfm_source_info;
87 /* Information about the data sink. */
88 struct stream_info vfm_sink_info;
90 /* Filter variable and `value' index. */
91 static struct variable *filter_var;
92 static int filter_index;
96 && (temp_case->data[filter_index].f == 0.0 \
97 || temp_case->data[filter_index].f == SYSMIS \
98 || is_num_user_missing (temp_case->data[filter_index].f, \
101 /* Nonzero if the case needs to have values deleted before being
102 stored, zero otherwise. */
103 int compaction_necessary;
105 /* Number of values after compaction, or the same as
106 vfm_sink_info.nval, if compaction is not necessary. */
109 /* Temporary case buffer with enough room for `compaction_nval'
111 struct ccase *compaction_case;
113 /* Within a session, when paging is turned on, it is never turned back
114 off. This policy might be too aggressive. */
115 static int paging = 0;
117 /* Time at which vfm was last invoked. */
118 time_t last_vfm_invocation;
120 /* Functions called during procedure processing. */
121 static int (*proc_func) (struct ccase *); /* Called for each case. */
122 static int (*virt_proc_func) (struct ccase *); /* From SPLIT_FILE_procfunc. */
123 static void (*begin_func) (void); /* Called at beginning of a series. */
124 static void (*virt_begin_func) (void); /* Called by SPLIT_FILE_procfunc. */
125 static void (*end_func) (void); /* Called after end of a series. */
126 int (*write_case) (void);
128 /* Number of cases passed to proc_func(). */
129 static int case_count;
132 int n_lag; /* Number of cases to lag. */
133 static int lag_count; /* Number of cases in lag_queue so far. */
134 static int lag_head; /* Index where next case will be added. */
135 static struct ccase **lag_queue; /* Array of n_lag ccase * elements. */
137 static void open_active_file (void);
138 static void close_active_file (void);
139 static int SPLIT_FILE_procfunc (struct ccase *);
140 static void finish_compaction (void);
141 static void lag_case (void);
142 static int procedure_write_case (void);
144 /* Public functions. */
146 /* Reads all the cases from the active file, transforms them by the
147 active set of transformations, calls PROCFUNC with CURCASE set to
148 the case and CASENUM set to the case number, and writes them to a
151 Divides the active file into zero or more series of one or more
152 cases each. BEGINFUNC is called before each series. ENDFUNC is
153 called after each series. */
155 procedure (void (*beginfunc) (void),
156 int (*procfunc) (struct ccase *curcase),
157 void (*endfunc) (void))
160 write_case = procedure_write_case;
162 if (default_dict.n_splits && procfunc != NULL)
164 virt_proc_func = procfunc;
165 proc_func = SPLIT_FILE_procfunc;
167 virt_begin_func = beginfunc;
170 begin_func = beginfunc;
171 proc_func = procfunc;
174 last_vfm_invocation = time (NULL);
178 close_active_file ();
181 /* Active file processing support. Subtly different semantics from
184 static int process_active_file_write_case (void);
186 /* The casefunc might want us to stop calling it. */
187 static int not_canceled;
189 /* Reads all the cases from the active file and passes them one-by-one
190 to CASEFUNC in temp_case. Before any cases are passed, calls
191 BEGINFUNC. After all the cases have been passed, calls ENDFUNC.
192 BEGINFUNC, CASEFUNC, and ENDFUNC can write temp_case to the output
193 file by calling process_active_file_output_case().
195 process_active_file() ignores TEMPORARY, SPLIT FILE, and N. */
197 process_active_file (void (*beginfunc) (void),
198 int (*casefunc) (struct ccase *curcase),
199 void (*endfunc) (void))
201 proc_func = casefunc;
202 write_case = process_active_file_write_case;
208 /* There doesn't necessarily need to be an active file. */
213 close_active_file ();
216 /* Pass the current case to casefunc. */
218 process_active_file_write_case (void)
220 /* Index of current transformation. */
223 for (cur_trns = f_trns ; cur_trns != temp_trns; )
227 code = t_trns[cur_trns]->proc (t_trns[cur_trns], temp_case);
231 /* Next transformation. */
235 /* Delete this case. */
238 /* Go to that transformation. */
247 /* Call the procedure if FILTER and PROCESS IF don't prohibit it. */
250 && (process_if_expr == NULL ||
251 expr_evaluate (process_if_expr, temp_case, NULL) == 1.0))
252 not_canceled = proc_func (temp_case);
260 /* This case is finished. Initialize the variables for the next case. */
261 for (lp = reinit_sysmis.vec; *lp != -1;)
262 temp_case->data[*lp++].f = SYSMIS;
263 for (lp = reinit_blanks.vec; *lp != -1;)
264 memset (temp_case->data[*lp++].s, ' ', MAX_SHORT_STRING);
270 /* Write temp_case to the active file. */
272 process_active_file_output_case (void)
274 vfm_sink_info.ncases++;
278 /* Opening the active file. */
280 /* It might be usefully noted that the following several functions are
281 given in the order that they are called by open_active_file(). */
283 /* Prepare to write to the replacement active file. */
285 prepare_for_writing (void)
287 /* FIXME: If ALL the conditions listed below hold true, then the
288 replacement active file is guaranteed to be identical to the
289 original active file:
291 1. TEMPORARY was the first transformation, OR, there were no
292 transformations at all.
294 2. Input is not coming from an input program.
296 3. Compaction is not necessary.
298 So, in this case, we shouldn't have to replace the active
299 file--it's just a waste of time and space. */
301 vfm_sink_info.ncases = 0;
302 vfm_sink_info.nval = default_dict.nval;
303 vfm_sink_info.case_size = (sizeof (struct ccase)
304 + (default_dict.nval - 1) * sizeof (union value));
306 if (vfm_sink == NULL)
308 if (vfm_sink_info.case_size * vfm_source_info.ncases > MAX_WORKSPACE
311 msg (MW, _("Workspace overflow predicted. Max workspace is "
312 "currently set to %d KB (%d cases at %d bytes each). "
313 "Paging active file to disk."),
314 MAX_WORKSPACE / 1024, MAX_WORKSPACE / vfm_sink_info.case_size,
315 vfm_sink_info.case_size);
320 vfm_sink = paging ? &vfm_disk_stream : &vfm_memory_stream;
324 /* Arrange for compacting the output cases for storage. */
326 arrange_compaction (void)
328 int count_values = 0;
333 /* Count up the number of `value's that will be output. */
334 for (i = 0; i < temp_dict->nvar; i++)
335 if (temp_dict->var[i]->name[0] != '#')
337 assert (temp_dict->var[i]->nv > 0);
338 count_values += temp_dict->var[i]->nv;
340 assert (temporary == 2 || count_values <= temp_dict->nval);
343 /* Compaction is only necessary if the number of `value's to output
344 differs from the number already present. */
345 compaction_nval = count_values;
346 compaction_necessary = temporary == 2 || count_values != temp_dict->nval;
352 /* Prepares the temporary case and compaction case. */
354 make_temp_case (void)
356 temp_case = xmalloc (vfm_sink_info.case_size);
358 if (compaction_necessary)
359 compaction_case = xmalloc (sizeof (struct ccase)
360 + sizeof (union value) * (compaction_nval - 1));
364 /* Returns the name of the variable that owns the index CCASE_INDEX
367 index_to_varname (int ccase_index)
371 for (i = 0; i < default_dict.nvar; i++)
373 variable *v = default_dict.var[i];
375 if (ccase_index >= v->fv && ccase_index < v->fv + v->nv)
376 return default_dict.var[i]->name;
382 /* Initializes temp_case from the vectors that say which `value's need
383 to be initialized just once, and which ones need to be
384 re-initialized before every case. */
386 vector_initialization (void)
392 for (i = 0; i < init_zero.n; i++)
393 temp_case->data[init_zero.vec[i]].f = 0.0;
394 for (i = 0; i < init_blanks.n; i++)
395 memset (temp_case->data[init_blanks.vec[i]].s, ' ', MAX_SHORT_STRING);
397 /* These vectors need to be repeatedly accessed, so we add a
398 sentinel to (hopefully) improve speed. */
399 vec_insert (&reinit_sysmis, -1);
400 vec_insert (&reinit_blanks, -1);
402 for (lp = reinit_sysmis.vec; *lp != -1;)
403 temp_case->data[*lp++].f = SYSMIS;
404 for (lp = reinit_blanks.vec; *lp != -1;)
405 memset (temp_case->data[*lp++].s, ' ', MAX_SHORT_STRING);
408 printf ("vfm: init_zero=");
409 for (i = 0; i < init_zero.n; i++)
410 printf ("%s%s", i ? "," : "", index_to_varname (init_zero.vec[i]));
411 printf (" init_blanks=");
412 for (i = 0; i < init_blanks.n; i++)
413 printf ("%s%s", i ? "," : "", index_to_varname (init_blanks.vec[i]));
414 printf (" reinit_sysmis=");
415 for (lp = reinit_sysmis.vec; *lp != -1; lp++)
416 printf ("%s%s", lp != reinit_sysmis.vec ? "," : "",
417 index_to_varname (*lp));
418 printf (" reinit_blanks=");
419 for (lp = reinit_blanks.vec; *lp != -1; lp++)
420 printf ("%s%s", lp != reinit_blanks.vec ? "," : "",
421 index_to_varname (*lp));
426 /* Sets filter_index to an appropriate value. */
432 if (default_dict.filter_var[0])
434 struct variable *fv = find_variable (default_dict.filter_var);
436 if (fv == NULL || fv->type == ALPHA)
437 default_dict.filter_var[0] = 0;
440 filter_index = fv->index;
446 /* Sets all the lag-related variables based on value of n_lag. */
457 lag_queue = xmalloc (n_lag * sizeof *lag_queue);
458 for (i = 0; i < n_lag; i++)
459 lag_queue[i] = xmalloc (temp_dict->nval * 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 ();
520 discard_ctl_stack ();
525 debug_printf (("vfm: reading from %s source, writing to %s sink.\n",
526 vfm_source->name, vfm_sink->name));
527 debug_printf (("vfm: vfm_source_info.nval=%d, vfm_sink_info.nval=%d, "
528 "temp_dict->nval=%d, compaction_nval=%d, "
529 "default_dict.nval=%d\n",
530 vfm_source_info.nval, vfm_sink_info.nval, temp_dict->nval,
531 compaction_nval, default_dict.nval));
534 /* Closes the active file. */
536 close_active_file (void)
538 /* Close the current case group. */
539 if (case_count && end_func != NULL)
542 /* Stop lagging (catch up?). */
547 for (i = 0; i < n_lag; i++)
553 /* Assume the dictionary from right before TEMPORARY, if any. Turn
557 restore_dictionary (temp_dict);
561 /* The default dictionary assumes the compacted data size. */
562 default_dict.nval = compaction_nval;
564 /* Old data sink --> New data source. */
565 if (vfm_source && vfm_source->destroy_source)
566 vfm_source->destroy_source ();
568 vfm_source = vfm_sink;
569 vfm_source_info.ncases = vfm_sink_info.ncases;
570 vfm_source_info.nval = compaction_nval;
571 vfm_source_info.case_size = (sizeof (struct ccase)
572 + (compaction_nval - 1) * sizeof (union value));
573 if (vfm_source->mode)
576 /* Old data sink is gone now. */
579 /* Finish compaction. */
580 if (compaction_necessary)
581 finish_compaction ();
584 /* Free temporary cases. */
588 free (compaction_case);
589 compaction_case = NULL;
591 /* Cancel PROCESS IF. */
592 expr_free (process_if_expr);
593 process_if_expr = NULL;
595 /* Cancel FILTER if temporary. */
596 if (filter_index != -1 && !FILTER_before_TEMPORARY)
597 default_dict.filter_var[0] = 0;
599 /* Cancel transformations. */
600 cancel_transformations ();
602 /* Clear value-initialization vectors. */
603 vec_clear (&init_zero);
604 vec_clear (&init_blanks);
605 vec_clear (&reinit_sysmis);
606 vec_clear (&reinit_blanks);
608 /* Turn off case limiter. */
611 /* Clear VECTOR vectors. */
615 for (i = 0; i < nvec; i++)
622 debug_printf (("vfm: procedure complete\n\n"));
625 /* Disk case stream. */
627 /* Associated files. */
628 FILE *disk_source_file;
629 FILE *disk_sink_file;
631 /* Initializes the disk sink. */
633 disk_stream_init (void)
635 disk_sink_file = tmpfile ();
638 msg (ME, _("An error occurred attempting to create a temporary "
639 "file for use as the active file: %s."),
645 /* Reads all cases from the disk source and passes them one by one to
648 disk_stream_read (void)
652 for (i = 0; i < vfm_source_info.ncases; i++)
654 if (!fread (temp_case, vfm_source_info.case_size, 1, disk_source_file))
656 msg (ME, _("An error occurred while attempting to read from "
657 "a temporary file created for the active file: %s."),
668 /* Writes temp_case to the disk sink. */
670 disk_stream_write (void)
672 union value *src_case;
674 if (compaction_necessary)
676 compact_case (compaction_case, temp_case);
677 src_case = (union value *) compaction_case;
679 else src_case = (union value *) temp_case;
681 if (fwrite (src_case, sizeof *src_case * compaction_nval, 1,
682 disk_sink_file) != 1)
684 msg (ME, _("An error occurred while attempting to write to a "
685 "temporary file used as the active file: %s."),
691 /* Switches the stream from a sink to a source. */
693 disk_stream_mode (void)
695 /* Rewind the sink. */
696 if (fseek (disk_sink_file, 0, SEEK_SET) != 0)
698 msg (ME, _("An error occurred while attempting to rewind a "
699 "temporary file used as the active file: %s."),
704 /* Sink --> source variables. */
705 disk_source_file = disk_sink_file;
708 /* Destroys the source's internal data. */
710 disk_stream_destroy_source (void)
712 if (disk_source_file)
714 fclose (disk_source_file);
715 disk_source_file = NULL;
719 /* Destroys the sink's internal data. */
721 disk_stream_destroy_sink (void)
725 fclose (disk_sink_file);
726 disk_sink_file = NULL;
731 struct case_stream vfm_disk_stream =
737 disk_stream_destroy_source,
738 disk_stream_destroy_sink,
742 /* Memory case stream. */
744 /* List of cases stored in the stream. */
745 struct case_list *memory_source_cases;
746 struct case_list *memory_sink_cases;
749 struct case_list *memory_sink_iter;
751 /* Maximum number of cases. */
752 int memory_sink_max_cases;
754 /* Initializes the memory stream variables for writing. */
756 memory_stream_init (void)
758 memory_sink_cases = NULL;
759 memory_sink_iter = NULL;
761 assert (compaction_nval);
762 memory_sink_max_cases = MAX_WORKSPACE / (sizeof (union value) * compaction_nval);
765 /* Reads the case stream from memory and passes it to write_case(). */
767 memory_stream_read (void)
769 while (memory_source_cases != NULL)
771 memcpy (temp_case, &memory_source_cases->c, vfm_source_info.case_size);
774 struct case_list *current = memory_source_cases;
775 memory_source_cases = memory_source_cases->next;
784 /* Writes temp_case to the memory stream. */
786 memory_stream_write (void)
788 struct case_list *new_case = malloc (sizeof (struct case_list)
789 + ((compaction_nval - 1)
790 * sizeof (union value)));
792 /* If we've got memory to spare then add it to the linked list. */
793 if (vfm_sink_info.ncases <= memory_sink_max_cases && new_case != NULL)
795 if (compaction_necessary)
796 compact_case (&new_case->c, temp_case);
798 memcpy (&new_case->c, temp_case, sizeof (union value) * compaction_nval);
800 /* Append case to linked list. */
801 if (memory_sink_cases)
802 memory_sink_iter = memory_sink_iter->next = new_case;
804 memory_sink_iter = memory_sink_cases = new_case;
808 /* Out of memory. Write the active file to disk. */
809 struct case_list *cur, *next;
811 /* Notify the user. */
813 msg (MW, _("Virtual memory exhausted. Paging active file "
816 msg (MW, _("Workspace limit of %d KB (%d cases at %d bytes each) "
817 "overflowed. Paging active file to disk."),
818 MAX_WORKSPACE / 1024, memory_sink_max_cases,
819 compaction_nval * sizeof (union value));
823 /* Switch to a disk sink. */
824 vfm_sink = &vfm_disk_stream;
828 /* Terminate the list. */
829 if (memory_sink_iter)
830 memory_sink_iter->next = NULL;
832 /* Write the cases to disk and destroy them. We can't call
833 vfm->sink->write() because of compaction. */
834 for (cur = memory_sink_cases; cur; cur = next)
837 if (fwrite (cur->c.data, sizeof (union value) * compaction_nval, 1,
838 disk_sink_file) != 1)
840 msg (ME, _("An error occurred while attempting to "
841 "write to a temporary file created as the "
842 "active file, while paging to disk: %s."),
849 /* Write the current case to disk. */
854 /* If the data is stored in memory, causes it to be written to disk.
855 To be called only *between* procedure()s, not within them. */
859 if (vfm_source == &vfm_memory_stream)
861 /* Switch to a disk sink. */
862 vfm_sink = &vfm_disk_stream;
866 /* Write the cases to disk and destroy them. We can't call
867 vfm->sink->write() because of compaction. */
869 struct case_list *cur, *next;
871 for (cur = memory_source_cases; cur; cur = next)
874 if (fwrite (cur->c.data, sizeof *cur->c.data * compaction_nval, 1,
875 disk_sink_file) != 1)
877 msg (ME, _("An error occurred while attempting to "
878 "write to a temporary file created as the "
879 "active file, while paging to disk: %s."),
887 vfm_source = &vfm_disk_stream;
894 /* Switch the memory stream from sink to source mode. */
896 memory_stream_mode (void)
898 /* Terminate the list. */
899 if (memory_sink_iter)
900 memory_sink_iter->next = NULL;
902 /* Sink --> source variables. */
903 memory_source_cases = memory_sink_cases;
904 memory_sink_cases = NULL;
907 /* Destroy all memory source data. */
909 memory_stream_destroy_source (void)
911 struct case_list *cur, *next;
913 for (cur = memory_source_cases; cur; cur = next)
918 memory_source_cases = NULL;
921 /* Destroy all memory sink data. */
923 memory_stream_destroy_sink (void)
925 struct case_list *cur, *next;
927 for (cur = memory_sink_cases; cur; cur = next)
932 memory_sink_cases = NULL;
936 struct case_stream vfm_memory_stream =
942 memory_stream_destroy_source,
943 memory_stream_destroy_sink,
948 #include "debug-print.h"
950 /* Add temp_case to the lag queue. */
954 if (lag_count < n_lag)
956 memcpy (lag_queue[lag_head], temp_case, sizeof (union value) * temp_dict->nval);
957 if (++lag_head >= n_lag)
961 /* Returns a pointer to the lagged case from N_BEFORE cases before the
962 current one, or NULL if there haven't been that many cases yet. */
964 lagged_case (int n_before)
966 assert (n_before <= n_lag);
967 if (n_before > lag_count)
971 int index = lag_head - n_before;
974 return lag_queue[index];
978 /* Transforms temp_case and writes it to the replacement active file
979 if advisable. Returns nonzero if more cases can be accepted, zero
980 otherwise. Do not call this function again after it has returned
983 procedure_write_case (void)
985 /* Index of current transformation. */
988 /* Return value: whether it's reasonable to write any more cases. */
991 debug_printf ((_("transform: ")));
996 /* Output the case if this is temp_trns. */
997 if (cur_trns == temp_trns)
999 debug_printf (("REC"));
1004 vfm_sink_info.ncases++;
1008 more_cases = vfm_sink_info.ncases < default_dict.N;
1012 if (cur_trns >= n_trns)
1015 debug_printf (("$%d", cur_trns));
1017 /* Decide which transformation should come next. */
1021 code = t_trns[cur_trns]->proc (t_trns[cur_trns], temp_case);
1025 /* Next transformation. */
1029 /* Delete this case. */
1032 /* Go to that transformation. */
1039 /* Call the beginning of group function. */
1040 if (!case_count && begin_func != NULL)
1043 /* Call the procedure if there is one and FILTER and PROCESS IF
1044 don't prohibit it. */
1045 if (proc_func != NULL
1047 && (process_if_expr == NULL ||
1048 expr_evaluate (process_if_expr, temp_case, NULL) == 1.0))
1049 proc_func (temp_case);
1054 debug_putc ('\n', stdout);
1059 /* This case is finished. Initialize the variables for the next case. */
1060 for (lp = reinit_sysmis.vec; *lp != -1;)
1061 temp_case->data[*lp++].f = SYSMIS;
1062 for (lp = reinit_blanks.vec; *lp != -1;)
1063 memset (temp_case->data[*lp++].s, ' ', MAX_SHORT_STRING);
1066 /* Return previously determined value. */
1070 /* Appends TRNS to t_trns[], the list of all transformations to be
1071 performed on data as it is read from the active file. */
1073 add_transformation (struct trns_header * trns)
1075 if (n_trns >= m_trns)
1078 t_trns = xrealloc (t_trns, sizeof *t_trns * m_trns);
1080 t_trns[n_trns] = trns;
1081 trns->index = n_trns++;
1084 /* Cancels all active transformations, including any transformations
1085 created by the input program. */
1087 cancel_transformations (void)
1090 for (i = 0; i < n_trns; i++)
1092 if (t_trns[i]->free)
1093 t_trns[i]->free (t_trns[i]);
1096 n_trns = f_trns = 0;
1104 /* Dumps out the values of all the split variables for the case C. */
1106 dump_splits (struct ccase *c)
1108 struct variable **iter;
1109 struct tab_table *t;
1112 t = tab_create (3, default_dict.n_splits + 1, 0);
1113 tab_dim (t, tab_natural_dimensions);
1114 tab_vline (t, TAL_1 | TAL_SPACING, 1, 0, default_dict.n_splits);
1115 tab_vline (t, TAL_1 | TAL_SPACING, 2, 0, default_dict.n_splits);
1116 tab_text (t, 0, 0, TAB_NONE, _("Variable"));
1117 tab_text (t, 1, 0, TAB_LEFT, _("Value"));
1118 tab_text (t, 2, 0, TAB_LEFT, _("Label"));
1119 for (iter = default_dict.splits, i = 0; *iter; iter++, i++)
1121 struct variable *v = *iter;
1125 assert (v->type == NUMERIC || v->type == ALPHA);
1126 tab_text (t, 0, i + 1, TAB_LEFT | TAT_PRINTF, "%s", v->name);
1129 union value val = c->data[v->fv];
1130 if (v->type == ALPHA)
1131 val.c = c->data[v->fv].s;
1132 data_out (temp_buf, &v->print, &val);
1135 temp_buf[v->print.w] = 0;
1136 tab_text (t, 1, i + 1, TAT_PRINTF, "%.*s", v->print.w, temp_buf);
1138 val_lab = get_val_lab (v, c->data[v->fv], 0);
1140 tab_text (t, 2, i + 1, TAB_LEFT, val_lab);
1142 tab_flags (t, SOMF_NO_TITLE);
1146 /* This procfunc is substituted for the user-supplied procfunc when
1147 SPLIT FILE is active. This function forms a wrapper around that
1148 procfunc by dividing the input into series. */
1150 SPLIT_FILE_procfunc (struct ccase *c)
1152 static struct ccase *prev_case;
1153 struct variable **iter;
1155 /* The first case always begins a new series. We also need to
1156 preserve the values of the case for later comparison. */
1157 if (case_count == 0)
1161 prev_case = xmalloc (vfm_sink_info.case_size);
1162 memcpy (prev_case, c, vfm_sink_info.case_size);
1165 if (virt_begin_func != NULL)
1168 return virt_proc_func (c);
1171 /* Compare the value of each SPLIT FILE variable to the values on
1172 the previous case. */
1173 for (iter = default_dict.splits; *iter; iter++)
1175 struct variable *v = *iter;
1180 if (approx_ne (c->data[v->fv].f, prev_case->data[v->fv].f))
1184 if (memcmp (c->data[v->fv].s, prev_case->data[v->fv].s, v->width))
1191 return virt_proc_func (c);
1194 /* The values of the SPLIT FILE variable are different from the
1195 values on the previous case. That means that it's time to begin
1197 if (end_func != NULL)
1200 if (virt_begin_func != NULL)
1202 memcpy (prev_case, c, vfm_sink_info.case_size);
1203 return virt_proc_func (c);
1206 /* Case compaction. */
1208 /* Copies case SRC to case DEST, compacting it in the process. */
1210 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 for (i = 0; i < default_dict.nvar; i++)
1228 struct variable *v = default_dict.var[i];
1230 if (v->name[0] == '#')
1233 if (v->type == NUMERIC)
1234 dest->data[nval++] = src->data[v->fv];
1237 int w = DIV_RND_UP (v->width, sizeof (union value));
1239 memcpy (&dest->data[nval], &src->data[v->fv], w * sizeof (union value));
1245 /* Reassigns `fv' for each variable. Deletes scratch variables. */
1247 finish_compaction (void)
1253 for (i = 0; i < default_dict.nvar; i++)
1255 struct variable *v = default_dict.var[i];
1257 if (v->name[0] == '#')
1259 clear_variable (&default_dict, v);
1265 if (v->type == NUMERIC)
1268 nval += DIV_RND_UP (v->width, sizeof (union value));
1270 default_dict.var[copy_index++] = v;
1272 if (copy_index != default_dict.nvar)
1274 default_dict.var = xrealloc (default_dict.var,
1275 sizeof *default_dict.var * copy_index);
1276 default_dict.nvar = copy_index;