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));
363 /* Initialize the unused trailing parts of string variables to avoid
364 spurious warnings from Checker. */
368 for (i = 0; i < default_dict.nvar; i++)
370 struct variable *v = default_dict.var[i];
372 if (v->type == ALPHA && v->width % 8 != 0)
373 memcpy (&temp_case->data[v->fv + v->nv - 1]
374 .s[v->width % 8], _("!ERROR!"), 8 - v->width % 8);
381 /* Returns the name of the variable that owns the index CCASE_INDEX
384 index_to_varname (int ccase_index)
388 for (i = 0; i < default_dict.nvar; i++)
390 variable *v = default_dict.var[i];
392 if (ccase_index >= v->fv && ccase_index < v->fv + v->nv)
393 return default_dict.var[i]->name;
399 /* Initializes temp_case from the vectors that say which `value's need
400 to be initialized just once, and which ones need to be
401 re-initialized before every case. */
403 vector_initialization (void)
409 for (i = 0; i < init_zero.n; i++)
410 temp_case->data[init_zero.vec[i]].f = 0.0;
411 for (i = 0; i < init_blanks.n; i++)
412 memset (temp_case->data[init_blanks.vec[i]].s, ' ', MAX_SHORT_STRING);
414 /* These vectors need to be repeatedly accessed, so we add a
415 sentinel to (hopefully) improve speed. */
416 vec_insert (&reinit_sysmis, -1);
417 vec_insert (&reinit_blanks, -1);
419 for (lp = reinit_sysmis.vec; *lp != -1;)
420 temp_case->data[*lp++].f = SYSMIS;
421 for (lp = reinit_blanks.vec; *lp != -1;)
422 memset (temp_case->data[*lp++].s, ' ', MAX_SHORT_STRING);
425 printf ("vfm: init_zero=");
426 for (i = 0; i < init_zero.n; i++)
427 printf ("%s%s", i ? "," : "", index_to_varname (init_zero.vec[i]));
428 printf (" init_blanks=");
429 for (i = 0; i < init_blanks.n; i++)
430 printf ("%s%s", i ? "," : "", index_to_varname (init_blanks.vec[i]));
431 printf (" reinit_sysmis=");
432 for (lp = reinit_sysmis.vec; *lp != -1; lp++)
433 printf ("%s%s", lp != reinit_sysmis.vec ? "," : "",
434 index_to_varname (*lp));
435 printf (" reinit_blanks=");
436 for (lp = reinit_blanks.vec; *lp != -1; lp++)
437 printf ("%s%s", lp != reinit_blanks.vec ? "," : "",
438 index_to_varname (*lp));
443 /* Sets filter_index to an appropriate value. */
449 if (default_dict.filter_var[0])
451 struct variable *fv = find_variable (default_dict.filter_var);
453 if (fv == NULL || fv->type == ALPHA)
454 default_dict.filter_var[0] = 0;
457 filter_index = fv->index;
463 /* Sets all the lag-related variables based on value of n_lag. */
474 lag_queue = xmalloc (n_lag * sizeof *lag_queue);
475 for (i = 0; i < n_lag; i++)
476 lag_queue[i] = xmalloc (temp_dict->nval * sizeof **lag_queue);
479 /* There is a lot of potential confusion in the vfm and related
480 routines over the number of `value's at each stage of the process.
481 Here is each nval count, with explanation, as set up by
484 vfm_source_info.nval: Number of `value's in the cases returned by
485 the source stream. This value turns out not to be very useful, but
486 we maintain it anyway.
488 vfm_sink_info.nval: Number of `value's in the cases after all
489 transformations have been performed. Never less than
490 vfm_source_info.nval.
492 temp_dict->nval: Number of `value's in the cases after the
493 transformations leading up to TEMPORARY have been performed. If
494 TEMPORARY was not specified, this is equal to vfm_sink_info.nval.
495 Never less than vfm_sink_info.nval.
497 compaction_nval: Number of `value's in the cases after the
498 transformations leading up to TEMPORARY have been performed and the
499 case has been compacted by compact_case(), if compaction is
500 necessary. This the number of `value's in the cases saved by the
501 sink stream. (However, note that the cases passed to the sink
502 stream have not yet been compacted. It is the responsibility of
503 the data sink to call compact_case().) This may be less than,
504 greater than, or equal to vfm_source_info.nval. `compaction'
505 becomes the new value of default_dict.nval after the procedure is
508 default_dict.nval: This is often an alias for temp_dict->nval. As
509 such it can really have no separate existence until the procedure
510 is complete. For this reason it should *not* be referenced inside
511 the execution of a procedure. */
512 /* Makes all preparations for reading from the data source and writing
515 open_active_file (void)
517 /* Sometimes we want to refer to the dictionary that applies to the
518 data actually written to the sink. This is either temp_dict or
519 default_dict. However, if TEMPORARY is not on, then temp_dict
520 does not apply. So, we can set temp_dict to default_dict in this
525 temp_dict = &default_dict;
528 /* No cases passed to the procedure yet. */
532 prepare_for_writing ();
533 arrange_compaction ();
535 vector_initialization ();
537 discard_ctl_stack ();
542 debug_printf (("vfm: reading from %s source, writing to %s sink.\n",
543 vfm_source->name, vfm_sink->name));
544 debug_printf (("vfm: vfm_source_info.nval=%d, vfm_sink_info.nval=%d, "
545 "temp_dict->nval=%d, compaction_nval=%d, "
546 "default_dict.nval=%d\n",
547 vfm_source_info.nval, vfm_sink_info.nval, temp_dict->nval,
548 compaction_nval, default_dict.nval));
551 /* Closes the active file. */
553 close_active_file (void)
555 /* Close the current case group. */
556 if (case_count && end_func != NULL)
559 /* Stop lagging (catch up?). */
564 for (i = 0; i < n_lag; i++)
570 /* Assume the dictionary from right before TEMPORARY, if any. Turn
574 restore_dictionary (temp_dict);
578 /* The default dictionary assumes the compacted data size. */
579 default_dict.nval = compaction_nval;
581 /* Old data sink --> New data source. */
582 if (vfm_source && vfm_source->destroy_source)
583 vfm_source->destroy_source ();
585 vfm_source = vfm_sink;
586 vfm_source_info.ncases = vfm_sink_info.ncases;
587 vfm_source_info.nval = compaction_nval;
588 vfm_source_info.case_size = (sizeof (struct ccase)
589 + (compaction_nval - 1) * sizeof (union value));
590 if (vfm_source->mode)
593 /* Old data sink is gone now. */
596 /* Finish compaction. */
597 if (compaction_necessary)
598 finish_compaction ();
601 /* Free temporary cases. */
605 free (compaction_case);
606 compaction_case = NULL;
608 /* Cancel PROCESS IF. */
609 expr_free (process_if_expr);
610 process_if_expr = NULL;
612 /* Cancel FILTER if temporary. */
613 if (filter_index != -1 && !FILTER_before_TEMPORARY)
614 default_dict.filter_var[0] = 0;
616 /* Cancel transformations. */
617 cancel_transformations ();
619 /* Clear value-initialization vectors. */
620 vec_clear (&init_zero);
621 vec_clear (&init_blanks);
622 vec_clear (&reinit_sysmis);
623 vec_clear (&reinit_blanks);
625 /* Turn off case limiter. */
628 /* Clear VECTOR vectors. */
632 for (i = 0; i < nvec; i++)
639 debug_printf (("vfm: procedure complete\n\n"));
642 /* Disk case stream. */
644 /* Associated files. */
645 FILE *disk_source_file;
646 FILE *disk_sink_file;
648 /* Initializes the disk sink. */
650 disk_stream_init (void)
652 disk_sink_file = tmpfile ();
655 msg (ME, _("An error occurred attempting to create a temporary "
656 "file for use as the active file: %s."),
662 /* Reads all cases from the disk source and passes them one by one to
665 disk_stream_read (void)
669 for (i = 0; i < vfm_source_info.ncases; i++)
671 if (!fread (temp_case, vfm_source_info.case_size, 1, disk_source_file))
673 msg (ME, _("An error occurred while attempting to read from "
674 "a temporary file created for the active file: %s."),
685 /* Writes temp_case to the disk sink. */
687 disk_stream_write (void)
689 union value *src_case;
691 if (compaction_necessary)
693 compact_case (compaction_case, temp_case);
694 src_case = (union value *) compaction_case;
696 else src_case = (union value *) temp_case;
698 if (fwrite (src_case, sizeof *src_case * compaction_nval, 1,
699 disk_sink_file) != 1)
701 msg (ME, _("An error occurred while attempting to write to a "
702 "temporary file used as the active file: %s."),
708 /* Switches the stream from a sink to a source. */
710 disk_stream_mode (void)
712 /* Rewind the sink. */
713 if (fseek (disk_sink_file, 0, SEEK_SET) != 0)
715 msg (ME, _("An error occurred while attempting to rewind a "
716 "temporary file used as the active file: %s."),
721 /* Sink --> source variables. */
722 disk_source_file = disk_sink_file;
725 /* Destroys the source's internal data. */
727 disk_stream_destroy_source (void)
729 if (disk_source_file)
731 fclose (disk_source_file);
732 disk_source_file = NULL;
736 /* Destroys the sink's internal data. */
738 disk_stream_destroy_sink (void)
742 fclose (disk_sink_file);
743 disk_sink_file = NULL;
748 struct case_stream vfm_disk_stream =
754 disk_stream_destroy_source,
755 disk_stream_destroy_sink,
759 /* Memory case stream. */
761 /* List of cases stored in the stream. */
762 struct case_list *memory_source_cases;
763 struct case_list *memory_sink_cases;
766 struct case_list *memory_sink_iter;
768 /* Maximum number of cases. */
769 int memory_sink_max_cases;
771 /* Initializes the memory stream variables for writing. */
773 memory_stream_init (void)
775 memory_sink_cases = NULL;
776 memory_sink_iter = NULL;
778 assert (compaction_nval);
779 memory_sink_max_cases = MAX_WORKSPACE / (sizeof (union value) * compaction_nval);
782 /* Reads the case stream from memory and passes it to write_case(). */
784 memory_stream_read (void)
786 while (memory_source_cases != NULL)
788 memcpy (temp_case, &memory_source_cases->c, vfm_source_info.case_size);
791 struct case_list *current = memory_source_cases;
792 memory_source_cases = memory_source_cases->next;
801 /* Writes temp_case to the memory stream. */
803 memory_stream_write (void)
805 struct case_list *new_case = malloc (sizeof (struct case_list)
806 + ((compaction_nval - 1)
807 * sizeof (union value)));
809 /* If we've got memory to spare then add it to the linked list. */
810 if (vfm_sink_info.ncases <= memory_sink_max_cases && new_case != NULL)
812 if (compaction_necessary)
813 compact_case (&new_case->c, temp_case);
815 memcpy (&new_case->c, temp_case, sizeof (union value) * compaction_nval);
817 /* Append case to linked list. */
818 if (memory_sink_cases)
819 memory_sink_iter = memory_sink_iter->next = new_case;
821 memory_sink_iter = memory_sink_cases = new_case;
825 /* Out of memory. Write the active file to disk. */
826 struct case_list *cur, *next;
828 /* Notify the user. */
830 msg (MW, _("Virtual memory exhausted. Paging active file "
833 msg (MW, _("Workspace limit of %d KB (%d cases at %d bytes each) "
834 "overflowed. Paging active file to disk."),
835 MAX_WORKSPACE / 1024, memory_sink_max_cases,
836 compaction_nval * sizeof (union value));
840 /* Switch to a disk sink. */
841 vfm_sink = &vfm_disk_stream;
845 /* Terminate the list. */
846 if (memory_sink_iter)
847 memory_sink_iter->next = NULL;
849 /* Write the cases to disk and destroy them. We can't call
850 vfm->sink->write() because of compaction. */
851 for (cur = memory_sink_cases; cur; cur = next)
854 if (fwrite (cur->c.data, sizeof (union value) * compaction_nval, 1,
855 disk_sink_file) != 1)
857 msg (ME, _("An error occurred while attempting to "
858 "write to a temporary file created as the "
859 "active file, while paging to disk: %s."),
866 /* Write the current case to disk. */
871 /* If the data is stored in memory, causes it to be written to disk.
872 To be called only *between* procedure()s, not within them. */
876 if (vfm_source == &vfm_memory_stream)
878 /* Switch to a disk sink. */
879 vfm_sink = &vfm_disk_stream;
883 /* Write the cases to disk and destroy them. We can't call
884 vfm->sink->write() because of compaction. */
886 struct case_list *cur, *next;
888 for (cur = memory_source_cases; cur; cur = next)
891 if (fwrite (cur->c.data, sizeof *cur->c.data * compaction_nval, 1,
892 disk_sink_file) != 1)
894 msg (ME, _("An error occurred while attempting to "
895 "write to a temporary file created as the "
896 "active file, while paging to disk: %s."),
904 vfm_source = &vfm_disk_stream;
911 /* Switch the memory stream from sink to source mode. */
913 memory_stream_mode (void)
915 /* Terminate the list. */
916 if (memory_sink_iter)
917 memory_sink_iter->next = NULL;
919 /* Sink --> source variables. */
920 memory_source_cases = memory_sink_cases;
921 memory_sink_cases = NULL;
924 /* Destroy all memory source data. */
926 memory_stream_destroy_source (void)
928 struct case_list *cur, *next;
930 for (cur = memory_source_cases; cur; cur = next)
935 memory_source_cases = NULL;
938 /* Destroy all memory sink data. */
940 memory_stream_destroy_sink (void)
942 struct case_list *cur, *next;
944 for (cur = memory_sink_cases; cur; cur = next)
949 memory_sink_cases = NULL;
953 struct case_stream vfm_memory_stream =
959 memory_stream_destroy_source,
960 memory_stream_destroy_sink,
965 #include "debug-print.h"
967 /* Add temp_case to the lag queue. */
971 if (lag_count < n_lag)
973 memcpy (lag_queue[lag_head], temp_case, sizeof (union value) * temp_dict->nval);
974 if (++lag_head >= n_lag)
978 /* Returns a pointer to the lagged case from N_BEFORE cases before the
979 current one, or NULL if there haven't been that many cases yet. */
981 lagged_case (int n_before)
983 assert (n_before <= n_lag);
984 if (n_before > lag_count)
988 int index = lag_head - n_before;
991 return lag_queue[index];
995 /* Transforms temp_case and writes it to the replacement active file
996 if advisable. Returns nonzero if more cases can be accepted, zero
997 otherwise. Do not call this function again after it has returned
1000 procedure_write_case (void)
1002 /* Index of current transformation. */
1005 /* Return value: whether it's reasonable to write any more cases. */
1008 debug_printf ((_("transform: ")));
1013 /* Output the case if this is temp_trns. */
1014 if (cur_trns == temp_trns)
1016 debug_printf (("REC"));
1021 vfm_sink_info.ncases++;
1025 more_cases = vfm_sink_info.ncases < default_dict.N;
1029 if (cur_trns >= n_trns)
1032 debug_printf (("$%d", cur_trns));
1034 /* Decide which transformation should come next. */
1038 code = t_trns[cur_trns]->proc (t_trns[cur_trns], temp_case);
1042 /* Next transformation. */
1046 /* Delete this case. */
1049 /* Go to that transformation. */
1056 /* Call the beginning of group function. */
1057 if (!case_count && begin_func != NULL)
1060 /* Call the procedure if there is one and FILTER and PROCESS IF
1061 don't prohibit it. */
1062 if (proc_func != NULL
1064 && (process_if_expr == NULL ||
1065 expr_evaluate (process_if_expr, temp_case, NULL) == 1.0))
1066 proc_func (temp_case);
1071 debug_putc ('\n', stdout);
1076 /* This case is finished. Initialize the variables for the next case. */
1077 for (lp = reinit_sysmis.vec; *lp != -1;)
1078 temp_case->data[*lp++].f = SYSMIS;
1079 for (lp = reinit_blanks.vec; *lp != -1;)
1080 memset (temp_case->data[*lp++].s, ' ', MAX_SHORT_STRING);
1083 /* Return previously determined value. */
1087 /* Appends TRNS to t_trns[], the list of all transformations to be
1088 performed on data as it is read from the active file. */
1090 add_transformation (struct trns_header * trns)
1092 if (n_trns >= m_trns)
1095 t_trns = xrealloc (t_trns, sizeof *t_trns * m_trns);
1097 t_trns[n_trns] = trns;
1098 trns->index = n_trns++;
1101 /* Cancels all active transformations, including any transformations
1102 created by the input program. */
1104 cancel_transformations (void)
1107 for (i = 0; i < n_trns; i++)
1109 if (t_trns[i]->free)
1110 t_trns[i]->free (t_trns[i]);
1113 n_trns = f_trns = 0;
1121 /* Dumps out the values of all the split variables for the case C. */
1123 dump_splits (struct ccase *c)
1125 struct variable **iter;
1126 struct tab_table *t;
1129 t = tab_create (3, default_dict.n_splits + 1, 0);
1130 tab_dim (t, tab_natural_dimensions);
1131 tab_vline (t, TAL_1 | TAL_SPACING, 1, 0, default_dict.n_splits);
1132 tab_vline (t, TAL_1 | TAL_SPACING, 2, 0, default_dict.n_splits);
1133 tab_text (t, 0, 0, TAB_NONE, _("Variable"));
1134 tab_text (t, 1, 0, TAB_LEFT, _("Value"));
1135 tab_text (t, 2, 0, TAB_LEFT, _("Label"));
1136 for (iter = default_dict.splits, i = 0; *iter; iter++, i++)
1138 struct variable *v = *iter;
1142 assert (v->type == NUMERIC || v->type == ALPHA);
1143 tab_text (t, 0, i + 1, TAB_LEFT | TAT_PRINTF, "%s", v->name);
1146 union value val = c->data[v->fv];
1147 if (v->type == ALPHA)
1148 val.c = c->data[v->fv].s;
1149 data_out (temp_buf, &v->print, &val);
1152 temp_buf[v->print.w] = 0;
1153 tab_text (t, 1, i + 1, TAT_PRINTF, "%.*s", v->print.w, temp_buf);
1155 val_lab = get_val_lab (v, c->data[v->fv], 0);
1157 tab_text (t, 2, i + 1, TAB_LEFT, val_lab);
1159 tab_flags (t, SOMF_NO_TITLE);
1163 /* This procfunc is substituted for the user-supplied procfunc when
1164 SPLIT FILE is active. This function forms a wrapper around that
1165 procfunc by dividing the input into series. */
1167 SPLIT_FILE_procfunc (struct ccase *c)
1169 static struct ccase *prev_case;
1170 struct variable **iter;
1172 /* The first case always begins a new series. We also need to
1173 preserve the values of the case for later comparison. */
1174 if (case_count == 0)
1178 prev_case = xmalloc (vfm_sink_info.case_size);
1179 memcpy (prev_case, c, vfm_sink_info.case_size);
1182 if (virt_begin_func != NULL)
1185 return virt_proc_func (c);
1188 /* Compare the value of each SPLIT FILE variable to the values on
1189 the previous case. */
1190 for (iter = default_dict.splits; *iter; iter++)
1192 struct variable *v = *iter;
1197 if (approx_ne (c->data[v->fv].f, prev_case->data[v->fv].f))
1201 if (memcmp (c->data[v->fv].s, prev_case->data[v->fv].s, v->width))
1208 return virt_proc_func (c);
1211 /* The values of the SPLIT FILE variable are different from the
1212 values on the previous case. That means that it's time to begin
1214 if (end_func != NULL)
1217 if (virt_begin_func != NULL)
1219 memcpy (prev_case, c, vfm_sink_info.case_size);
1220 return virt_proc_func (c);
1223 /* Case compaction. */
1225 /* Copies case SRC to case DEST, compacting it in the process. */
1227 compact_case (struct ccase *dest, const struct ccase *src)
1232 assert (compaction_necessary);
1236 if (dest != compaction_case)
1237 memcpy (dest, compaction_case, sizeof (union value) * compaction_nval);
1241 /* Copy all the variables except the scratch variables from SRC to
1243 for (i = 0; i < default_dict.nvar; i++)
1245 struct variable *v = default_dict.var[i];
1247 if (v->name[0] == '#')
1250 if (v->type == NUMERIC)
1251 dest->data[nval++] = src->data[v->fv];
1254 int w = DIV_RND_UP (v->width, sizeof (union value));
1256 memcpy (&dest->data[nval], &src->data[v->fv], w * sizeof (union value));
1262 /* Reassigns `fv' for each variable. Deletes scratch variables. */
1264 finish_compaction (void)
1270 for (i = 0; i < default_dict.nvar; i++)
1272 struct variable *v = default_dict.var[i];
1274 if (v->name[0] == '#')
1276 clear_variable (&default_dict, v);
1282 if (v->type == NUMERIC)
1285 nval += DIV_RND_UP (v->width, sizeof (union value));
1287 default_dict.var[copy_index++] = v;
1289 if (copy_index != default_dict.nvar)
1291 default_dict.var = xrealloc (default_dict.var,
1292 sizeof *default_dict.var * copy_index);
1293 default_dict.nvar = copy_index;