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. */
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 (default_dict.n_splits && 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 = default_dict.nval;
301 vfm_sink_info.case_size = (sizeof (struct ccase)
302 + (default_dict.nval - 1) * sizeof (union value));
304 if (vfm_sink == NULL)
306 if (vfm_sink_info.case_size * vfm_source_info.ncases > MAX_WORKSPACE
309 msg (MW, _("Workspace overflow predicted. Max workspace is "
310 "currently set to %d KB (%d cases at %d bytes each). "
311 "Paging active file to disk."),
312 MAX_WORKSPACE / 1024, MAX_WORKSPACE / vfm_sink_info.case_size,
313 vfm_sink_info.case_size);
318 vfm_sink = paging ? &vfm_disk_stream : &vfm_memory_stream;
322 /* Arrange for compacting the output cases for storage. */
324 arrange_compaction (void)
326 int count_values = 0;
331 /* Count up the number of `value's that will be output. */
332 for (i = 0; i < temp_dict->nvar; i++)
333 if (temp_dict->var[i]->name[0] != '#')
335 assert (temp_dict->var[i]->nv > 0);
336 count_values += temp_dict->var[i]->nv;
338 assert (temporary == 2 || count_values <= temp_dict->nval);
341 /* Compaction is only necessary if the number of `value's to output
342 differs from the number already present. */
343 compaction_nval = count_values;
344 compaction_necessary = temporary == 2 || count_values != temp_dict->nval;
350 /* Prepares the temporary case and compaction case. */
352 make_temp_case (void)
354 temp_case = xmalloc (vfm_sink_info.case_size);
356 if (compaction_necessary)
357 compaction_case = xmalloc (sizeof (struct ccase)
358 + sizeof (union value) * (compaction_nval - 1));
362 /* Returns the name of the variable that owns the index CCASE_INDEX
365 index_to_varname (int ccase_index)
369 for (i = 0; i < default_dict.nvar; i++)
371 struct variable *v = default_dict.var[i];
373 if (ccase_index >= v->fv && ccase_index < v->fv + v->nv)
374 return default_dict.var[i]->name;
380 /* Initializes temp_case from the vectors that say which `value's need
381 to be initialized just once, and which ones need to be
382 re-initialized before every case. */
384 vector_initialization (void)
390 for (i = 0; i < init_zero.n; i++)
391 temp_case->data[init_zero.vec[i]].f = 0.0;
392 for (i = 0; i < init_blanks.n; i++)
393 memset (temp_case->data[init_blanks.vec[i]].s, ' ', MAX_SHORT_STRING);
395 /* These vectors need to be repeatedly accessed, so we add a
396 sentinel to (hopefully) improve speed. */
397 vec_insert (&reinit_sysmis, -1);
398 vec_insert (&reinit_blanks, -1);
400 for (lp = reinit_sysmis.vec; *lp != -1;)
401 temp_case->data[*lp++].f = SYSMIS;
402 for (lp = reinit_blanks.vec; *lp != -1;)
403 memset (temp_case->data[*lp++].s, ' ', MAX_SHORT_STRING);
406 printf ("vfm: init_zero=");
407 for (i = 0; i < init_zero.n; i++)
408 printf ("%s%s", i ? "," : "", index_to_varname (init_zero.vec[i]));
409 printf (" init_blanks=");
410 for (i = 0; i < init_blanks.n; i++)
411 printf ("%s%s", i ? "," : "", index_to_varname (init_blanks.vec[i]));
412 printf (" reinit_sysmis=");
413 for (lp = reinit_sysmis.vec; *lp != -1; lp++)
414 printf ("%s%s", lp != reinit_sysmis.vec ? "," : "",
415 index_to_varname (*lp));
416 printf (" reinit_blanks=");
417 for (lp = reinit_blanks.vec; *lp != -1; lp++)
418 printf ("%s%s", lp != reinit_blanks.vec ? "," : "",
419 index_to_varname (*lp));
424 /* Sets filter_index to an appropriate value. */
430 if (default_dict.filter_var[0])
432 struct variable *fv = find_variable (default_dict.filter_var);
434 if (fv == NULL || fv->type == ALPHA)
435 default_dict.filter_var[0] = 0;
438 filter_index = fv->index;
444 /* Sets all the lag-related variables based on value of n_lag. */
455 lag_queue = xmalloc (n_lag * sizeof *lag_queue);
456 for (i = 0; i < n_lag; i++)
457 lag_queue[i] = xmalloc (temp_dict->nval * sizeof **lag_queue);
460 /* There is a lot of potential confusion in the vfm and related
461 routines over the number of `value's at each stage of the process.
462 Here is each nval count, with explanation, as set up by
465 vfm_source_info.nval: Number of `value's in the cases returned by
466 the source stream. This value turns out not to be very useful, but
467 we maintain it anyway.
469 vfm_sink_info.nval: Number of `value's in the cases after all
470 transformations have been performed. Never less than
471 vfm_source_info.nval.
473 temp_dict->nval: Number of `value's in the cases after the
474 transformations leading up to TEMPORARY have been performed. If
475 TEMPORARY was not specified, this is equal to vfm_sink_info.nval.
476 Never less than vfm_sink_info.nval.
478 compaction_nval: Number of `value's in the cases after the
479 transformations leading up to TEMPORARY have been performed and the
480 case has been compacted by compact_case(), if compaction is
481 necessary. This the number of `value's in the cases saved by the
482 sink stream. (However, note that the cases passed to the sink
483 stream have not yet been compacted. It is the responsibility of
484 the data sink to call compact_case().) This may be less than,
485 greater than, or equal to vfm_source_info.nval. `compaction'
486 becomes the new value of default_dict.nval after the procedure is
489 default_dict.nval: This is often an alias for temp_dict->nval. As
490 such it can really have no separate existence until the procedure
491 is complete. For this reason it should *not* be referenced inside
492 the execution of a procedure. */
493 /* Makes all preparations for reading from the data source and writing
496 open_active_file (void)
498 /* Sometimes we want to refer to the dictionary that applies to the
499 data actually written to the sink. This is either temp_dict or
500 default_dict. However, if TEMPORARY is not on, then temp_dict
501 does not apply. So, we can set temp_dict to default_dict in this
506 temp_dict = &default_dict;
509 /* No cases passed to the procedure yet. */
513 prepare_for_writing ();
514 arrange_compaction ();
516 vector_initialization ();
518 discard_ctl_stack ();
523 debug_printf (("vfm: reading from %s source, writing to %s sink.\n",
524 vfm_source->name, vfm_sink->name));
525 debug_printf (("vfm: vfm_source_info.nval=%d, vfm_sink_info.nval=%d, "
526 "temp_dict->nval=%d, compaction_nval=%d, "
527 "default_dict.nval=%d\n",
528 vfm_source_info.nval, vfm_sink_info.nval, temp_dict->nval,
529 compaction_nval, default_dict.nval));
532 /* Closes the active file. */
534 close_active_file (void)
536 /* Close the current case group. */
537 if (case_count && end_func != NULL)
540 /* Stop lagging (catch up?). */
545 for (i = 0; i < n_lag; i++)
551 /* Assume the dictionary from right before TEMPORARY, if any. Turn
555 restore_dictionary (temp_dict);
559 /* The default dictionary assumes the compacted data size. */
560 default_dict.nval = compaction_nval;
562 /* Old data sink --> New data source. */
563 if (vfm_source && vfm_source->destroy_source)
564 vfm_source->destroy_source ();
566 vfm_source = vfm_sink;
567 vfm_source_info.ncases = vfm_sink_info.ncases;
568 vfm_source_info.nval = compaction_nval;
569 vfm_source_info.case_size = (sizeof (struct ccase)
570 + (compaction_nval - 1) * sizeof (union value));
571 if (vfm_source->mode)
574 /* Old data sink is gone now. */
577 /* Finish compaction. */
578 if (compaction_necessary)
579 finish_compaction ();
582 /* Free temporary cases. */
586 free (compaction_case);
587 compaction_case = NULL;
589 /* Cancel PROCESS IF. */
590 expr_free (process_if_expr);
591 process_if_expr = NULL;
593 /* Cancel FILTER if temporary. */
594 if (filter_index != -1 && !FILTER_before_TEMPORARY)
595 default_dict.filter_var[0] = 0;
597 /* Cancel transformations. */
598 cancel_transformations ();
600 /* Clear value-initialization vectors. */
601 vec_clear (&init_zero);
602 vec_clear (&init_blanks);
603 vec_clear (&reinit_sysmis);
604 vec_clear (&reinit_blanks);
606 /* Turn off case limiter. */
609 /* Clear VECTOR vectors. */
613 for (i = 0; i < nvec; i++)
620 debug_printf (("vfm: procedure complete\n\n"));
623 /* Disk case stream. */
625 /* Associated files. */
626 FILE *disk_source_file;
627 FILE *disk_sink_file;
629 /* Initializes the disk sink. */
631 disk_stream_init (void)
633 disk_sink_file = tmpfile ();
636 msg (ME, _("An error occurred attempting to create a temporary "
637 "file for use as the active file: %s."),
643 /* Reads all cases from the disk source and passes them one by one to
646 disk_stream_read (void)
650 for (i = 0; i < vfm_source_info.ncases; i++)
652 if (!fread (temp_case, vfm_source_info.case_size, 1, disk_source_file))
654 msg (ME, _("An error occurred while attempting to read from "
655 "a temporary file created for the active file: %s."),
666 /* Writes temp_case to the disk sink. */
668 disk_stream_write (void)
670 union value *src_case;
672 if (compaction_necessary)
674 compact_case (compaction_case, temp_case);
675 src_case = (union value *) compaction_case;
677 else src_case = (union value *) temp_case;
679 if (fwrite (src_case, sizeof *src_case * compaction_nval, 1,
680 disk_sink_file) != 1)
682 msg (ME, _("An error occurred while attempting to write to a "
683 "temporary file used as the active file: %s."),
689 /* Switches the stream from a sink to a source. */
691 disk_stream_mode (void)
693 /* Rewind the sink. */
694 if (fseek (disk_sink_file, 0, SEEK_SET) != 0)
696 msg (ME, _("An error occurred while attempting to rewind a "
697 "temporary file used as the active file: %s."),
702 /* Sink --> source variables. */
703 disk_source_file = disk_sink_file;
706 /* Destroys the source's internal data. */
708 disk_stream_destroy_source (void)
710 if (disk_source_file)
712 fclose (disk_source_file);
713 disk_source_file = NULL;
717 /* Destroys the sink's internal data. */
719 disk_stream_destroy_sink (void)
723 fclose (disk_sink_file);
724 disk_sink_file = NULL;
729 struct case_stream vfm_disk_stream =
735 disk_stream_destroy_source,
736 disk_stream_destroy_sink,
740 /* Memory case stream. */
742 /* List of cases stored in the stream. */
743 struct case_list *memory_source_cases;
744 struct case_list *memory_sink_cases;
747 struct case_list *memory_sink_iter;
749 /* Maximum number of cases. */
750 int memory_sink_max_cases;
752 /* Initializes the memory stream variables for writing. */
754 memory_stream_init (void)
756 memory_sink_cases = NULL;
757 memory_sink_iter = NULL;
759 assert (compaction_nval);
760 memory_sink_max_cases = MAX_WORKSPACE / (sizeof (union value) * compaction_nval);
763 /* Reads the case stream from memory and passes it to write_case(). */
765 memory_stream_read (void)
767 while (memory_source_cases != NULL)
769 memcpy (temp_case, &memory_source_cases->c, vfm_source_info.case_size);
772 struct case_list *current = memory_source_cases;
773 memory_source_cases = memory_source_cases->next;
782 /* Writes temp_case to the memory stream. */
784 memory_stream_write (void)
786 struct case_list *new_case = malloc (sizeof (struct case_list)
787 + ((compaction_nval - 1)
788 * sizeof (union value)));
790 /* If we've got memory to spare then add it to the linked list. */
791 if (vfm_sink_info.ncases <= memory_sink_max_cases && new_case != NULL)
793 if (compaction_necessary)
794 compact_case (&new_case->c, temp_case);
796 memcpy (&new_case->c, temp_case, sizeof (union value) * compaction_nval);
798 /* Append case to linked list. */
799 if (memory_sink_cases)
800 memory_sink_iter = memory_sink_iter->next = new_case;
802 memory_sink_iter = memory_sink_cases = new_case;
806 /* Out of memory. Write the active file to disk. */
807 struct case_list *cur, *next;
809 /* Notify the user. */
811 msg (MW, _("Virtual memory exhausted. Paging active file "
814 msg (MW, _("Workspace limit of %d KB (%d cases at %d bytes each) "
815 "overflowed. Paging active file to disk."),
816 MAX_WORKSPACE / 1024, memory_sink_max_cases,
817 compaction_nval * sizeof (union value));
821 /* Switch to a disk sink. */
822 vfm_sink = &vfm_disk_stream;
826 /* Terminate the list. */
827 if (memory_sink_iter)
828 memory_sink_iter->next = NULL;
830 /* Write the cases to disk and destroy them. We can't call
831 vfm->sink->write() because of compaction. */
832 for (cur = memory_sink_cases; cur; cur = next)
835 if (fwrite (cur->c.data, sizeof (union value) * compaction_nval, 1,
836 disk_sink_file) != 1)
838 msg (ME, _("An error occurred while attempting to "
839 "write to a temporary file created as the "
840 "active file, while paging to disk: %s."),
847 /* Write the current case to disk. */
852 /* If the data is stored in memory, causes it to be written to disk.
853 To be called only *between* procedure()s, not within them. */
857 if (vfm_source == &vfm_memory_stream)
859 /* Switch to a disk sink. */
860 vfm_sink = &vfm_disk_stream;
864 /* Write the cases to disk and destroy them. We can't call
865 vfm->sink->write() because of compaction. */
867 struct case_list *cur, *next;
869 for (cur = memory_source_cases; cur; cur = next)
872 if (fwrite (cur->c.data, sizeof *cur->c.data * compaction_nval, 1,
873 disk_sink_file) != 1)
875 msg (ME, _("An error occurred while attempting to "
876 "write to a temporary file created as the "
877 "active file, while paging to disk: %s."),
885 vfm_source = &vfm_disk_stream;
892 /* Switch the memory stream from sink to source mode. */
894 memory_stream_mode (void)
896 /* Terminate the list. */
897 if (memory_sink_iter)
898 memory_sink_iter->next = NULL;
900 /* Sink --> source variables. */
901 memory_source_cases = memory_sink_cases;
902 memory_sink_cases = NULL;
905 /* Destroy all memory source data. */
907 memory_stream_destroy_source (void)
909 struct case_list *cur, *next;
911 for (cur = memory_source_cases; cur; cur = next)
916 memory_source_cases = NULL;
919 /* Destroy all memory sink data. */
921 memory_stream_destroy_sink (void)
923 struct case_list *cur, *next;
925 for (cur = memory_sink_cases; cur; cur = next)
930 memory_sink_cases = NULL;
934 struct case_stream vfm_memory_stream =
940 memory_stream_destroy_source,
941 memory_stream_destroy_sink,
945 #include "debug-print.h"
947 /* Add temp_case to the lag queue. */
951 if (lag_count < n_lag)
953 memcpy (lag_queue[lag_head], temp_case, sizeof (union value) * temp_dict->nval);
954 if (++lag_head >= n_lag)
958 /* Returns a pointer to the lagged case from N_BEFORE cases before the
959 current one, or NULL if there haven't been that many cases yet. */
961 lagged_case (int n_before)
963 assert (n_before <= n_lag);
964 if (n_before > lag_count)
968 int index = lag_head - n_before;
971 return lag_queue[index];
975 /* Transforms temp_case and writes it to the replacement active file
976 if advisable. Returns nonzero if more cases can be accepted, zero
977 otherwise. Do not call this function again after it has returned
980 procedure_write_case (void)
982 /* Index of current transformation. */
985 /* Return value: whether it's reasonable to write any more cases. */
988 debug_printf ((_("transform: ")));
993 /* Output the case if this is temp_trns. */
994 if (cur_trns == temp_trns)
996 debug_printf (("REC"));
1001 vfm_sink_info.ncases++;
1005 more_cases = vfm_sink_info.ncases < default_dict.N;
1009 if (cur_trns >= n_trns)
1012 debug_printf (("$%d", cur_trns));
1014 /* Decide which transformation should come next. */
1018 code = t_trns[cur_trns]->proc (t_trns[cur_trns], temp_case);
1022 /* Next transformation. */
1026 /* Delete this case. */
1029 /* Go to that transformation. */
1036 /* Call the beginning of group function. */
1037 if (!case_count && begin_func != NULL)
1040 /* Call the procedure if there is one and FILTER and PROCESS IF
1041 don't prohibit it. */
1042 if (proc_func != NULL
1044 && (process_if_expr == NULL ||
1045 expr_evaluate (process_if_expr, temp_case, NULL) == 1.0))
1046 proc_func (temp_case);
1051 debug_putc ('\n', stdout);
1056 /* This case is finished. Initialize the variables for the next case. */
1057 for (lp = reinit_sysmis.vec; *lp != -1;)
1058 temp_case->data[*lp++].f = SYSMIS;
1059 for (lp = reinit_blanks.vec; *lp != -1;)
1060 memset (temp_case->data[*lp++].s, ' ', MAX_SHORT_STRING);
1063 /* Return previously determined value. */
1067 /* Appends TRNS to t_trns[], the list of all transformations to be
1068 performed on data as it is read from the active file. */
1070 add_transformation (struct trns_header * trns)
1072 if (n_trns >= m_trns)
1075 t_trns = xrealloc (t_trns, sizeof *t_trns * m_trns);
1077 t_trns[n_trns] = trns;
1078 trns->index = n_trns++;
1081 /* Cancels all active transformations, including any transformations
1082 created by the input program. */
1084 cancel_transformations (void)
1087 for (i = 0; i < n_trns; i++)
1089 if (t_trns[i]->free)
1090 t_trns[i]->free (t_trns[i]);
1093 n_trns = f_trns = 0;
1101 /* Dumps out the values of all the split variables for the case C. */
1103 dump_splits (struct ccase *c)
1105 struct variable **iter;
1106 struct tab_table *t;
1109 t = tab_create (3, default_dict.n_splits + 1, 0);
1110 tab_dim (t, tab_natural_dimensions);
1111 tab_vline (t, TAL_1 | TAL_SPACING, 1, 0, default_dict.n_splits);
1112 tab_vline (t, TAL_1 | TAL_SPACING, 2, 0, default_dict.n_splits);
1113 tab_text (t, 0, 0, TAB_NONE, _("Variable"));
1114 tab_text (t, 1, 0, TAB_LEFT, _("Value"));
1115 tab_text (t, 2, 0, TAB_LEFT, _("Label"));
1116 for (iter = default_dict.splits, i = 0; *iter; iter++, i++)
1118 struct variable *v = *iter;
1122 assert (v->type == NUMERIC || v->type == ALPHA);
1123 tab_text (t, 0, i + 1, TAB_LEFT | TAT_PRINTF, "%s", v->name);
1126 union value val = c->data[v->fv];
1127 if (v->type == ALPHA)
1128 val.c = c->data[v->fv].s;
1129 data_out (temp_buf, &v->print, &val);
1132 temp_buf[v->print.w] = 0;
1133 tab_text (t, 1, i + 1, TAT_PRINTF, "%.*s", v->print.w, temp_buf);
1135 val_lab = get_val_lab (v, c->data[v->fv], 0);
1137 tab_text (t, 2, i + 1, TAB_LEFT, val_lab);
1139 tab_flags (t, SOMF_NO_TITLE);
1143 /* This procfunc is substituted for the user-supplied procfunc when
1144 SPLIT FILE is active. This function forms a wrapper around that
1145 procfunc by dividing the input into series. */
1147 SPLIT_FILE_procfunc (struct ccase *c)
1149 static struct ccase *prev_case;
1150 struct variable **iter;
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 for (iter = default_dict.splits; *iter; iter++)
1172 struct variable *v = *iter;
1177 if (approx_ne (c->data[v->fv].f, prev_case->data[v->fv].f))
1181 if (memcmp (c->data[v->fv].s, prev_case->data[v->fv].s, v->width))
1188 return virt_proc_func (c);
1191 /* The values of the SPLIT FILE variable are different from the
1192 values on the previous case. That means that it's time to begin
1194 if (end_func != NULL)
1197 if (virt_begin_func != NULL)
1199 memcpy (prev_case, c, vfm_sink_info.case_size);
1200 return virt_proc_func (c);
1203 /* Case compaction. */
1205 /* Copies case SRC to case DEST, compacting it in the process. */
1207 compact_case (struct ccase *dest, const struct ccase *src)
1212 assert (compaction_necessary);
1216 if (dest != compaction_case)
1217 memcpy (dest, compaction_case, sizeof (union value) * compaction_nval);
1221 /* Copy all the variables except the scratch variables from SRC to
1223 for (i = 0; i < default_dict.nvar; i++)
1225 struct variable *v = default_dict.var[i];
1227 if (v->name[0] == '#')
1230 if (v->type == NUMERIC)
1231 dest->data[nval++] = src->data[v->fv];
1234 int w = DIV_RND_UP (v->width, sizeof (union value));
1236 memcpy (&dest->data[nval], &src->data[v->fv], w * sizeof (union value));
1242 /* Reassigns `fv' for each variable. Deletes scratch variables. */
1244 finish_compaction (void)
1250 for (i = 0; i < default_dict.nvar; i++)
1252 struct variable *v = default_dict.var[i];
1254 if (v->name[0] == '#')
1256 clear_variable (&default_dict, v);
1262 if (v->type == NUMERIC)
1265 nval += DIV_RND_UP (v->width, sizeof (union value));
1267 default_dict.var[copy_index++] = v;
1269 if (copy_index != default_dict.nvar)
1271 default_dict.var = xrealloc (default_dict.var,
1272 sizeof *default_dict.var * copy_index);
1273 default_dict.nvar = copy_index;