#include "debug-print.h"
+/* GET or IMPORT input program. */
+struct get_pgm
+ {
+ struct file_handle *handle; /* File to GET or IMPORT from. */
+ size_t case_size; /* Case size in bytes. */
+ };
+
/* XSAVE transformation (and related SAVE, EXPORT procedures). */
struct save_trns
{
static int trim_dictionary (struct dictionary * dict, int *options);
static int save_write_case_func (struct ccase *, void *);
-static int save_trns_proc (struct trns_header *, struct ccase *);
-static void save_trns_free (struct trns_header *);
+static trns_proc_func save_trns_proc;
+static trns_free_func save_trns_free;
#if DEBUGGING
void dump_dict_variables (struct dictionary *);
{
struct file_handle *handle;
struct dictionary *dict;
+ struct get_pgm *pgm;
int options = GTSV_OPT_NONE;
lex_match_id ("GET");
dict_destroy (default_dict);
default_dict = dict;
- vfm_source = create_case_source (&get_source_class, handle);
+ pgm = xmalloc (sizeof *pgm);
+ pgm->handle = handle;
+ pgm->case_size = dict_get_case_size (default_dict);
+ vfm_source = create_case_source (&get_source_class, default_dict, pgm);
return CMD_SUCCESS;
}
if (save_cmd == CMD_SAVE)
{
- procedure (NULL, save_write_case_func, NULL, t);
+ procedure (save_write_case_func, t);
save_trns_free (&t->h);
}
else
sfm_write_case (t->f, t->case_buf, p - t->case_buf);
}
+/* Writes case C to the system file specified on SAVE. */
static int
-save_write_case_func (struct ccase * c, void *aux UNUSED)
+save_write_case_func (struct ccase *c, void *aux UNUSED)
{
do_write_case (aux, c);
return 1;
}
+/* Writes case C to the system file specified on XSAVE. */
static int
-save_trns_proc (struct trns_header *h, struct ccase * c)
+save_trns_proc (struct trns_header *h, struct ccase *c, int case_num UNUSED)
{
struct save_trns *t = (struct save_trns *) h;
do_write_case (t, c);
return -1;
}
+/* Frees a SAVE transformation. */
static void
save_trns_free (struct trns_header *pt)
{
v = xmalloc (sizeof *v * dict_get_var_cnt (dict));
nv = 0;
for (i = 0; i < dict_get_var_cnt (dict); i++)
- if (dict_get_var (dict, i)->name[0] == '#')
+ if (dict_class_from_id (dict_get_var (dict, i)->name) == DC_SCRATCH)
v[nv++] = dict_get_var (dict, i);
dict_delete_vars (dict, v, nv);
free (v);
static void
get_source_destroy (struct case_source *source)
{
- struct file_handle *handle = source->aux;
+ struct get_pgm *pgm = source->aux;
/* It is not necessary to destroy the dictionary because if we get
to this point then the dictionary is default_dict. */
- fh_close_handle (handle);
+ fh_close_handle (pgm->handle);
+ free (pgm);
}
-/* Reads all the cases from the data file and passes them to
- write_case(). */
+/* Reads all the cases from the data file into C and passes them
+ to WRITE_CASE one by one, passing WC_DATA. */
static void
get_source_read (struct case_source *source,
+ struct ccase *c,
write_case_func *write_case, write_case_data wc_data)
{
- struct file_handle *handle = source->aux;
+ struct get_pgm *pgm = source->aux;
- while (sfm_read_case (handle, temp_case->data, default_dict)
+ while (sfm_read_case (pgm->handle, c->data, default_dict)
&& write_case (wc_data))
;
}
const struct case_source_class get_source_class =
{
"GET",
+ NULL,
get_source_read,
get_source_destroy,
};
/* Sequence numbers for each variable in mtf_master. */
static unsigned *mtf_seq_nums;
+/* Sink for MATCH FILES output. */
+static struct case_sink *mtf_sink;
+
+/* Case used for MATCH FILES output. */
+static struct ccase *mtf_case;
+
static void mtf_free (void);
static void mtf_free_file (struct mtf_file *file);
static int mtf_merge_dictionary (struct mtf_file *f);
"file has been defined."));
goto lossage;
}
+
+ if (temporary != 0)
+ {
+ msg (SE,
+ _("MATCH FILES may not be used after TEMPORARY when "
+ "the active file is an input source. "
+ "Temporary transformations will be made permanent."));
+ cancel_temporary ();
+ }
}
else
{
FIXME: For merging large numbers of files (more than 10?) a
better algorithm would use a heap for finding minimum
- values, or replacement selection, as described by Knuth in
- _Art of Computer Programming, Vol. 3_. The SORT CASES
- procedure does this, and perhaps some of its code could be
- adapted. */
+ values. */
if (!(seen & 2))
discard_variables ();
- temporary = 2;
- temp_dict = mtf_master;
- temp_trns = n_trns;
+ mtf_sink = create_case_sink (&storage_sink_class, mtf_master, NULL);
mtf_seq_nums = xmalloc (dict_get_var_cnt (mtf_master)
* sizeof *mtf_seq_nums);
memset (mtf_seq_nums, 0,
dict_get_var_cnt (mtf_master) * sizeof *mtf_seq_nums);
+ mtf_case = xmalloc (dict_get_case_size (mtf_master));
+
+ mtf_read_nonactive_records (NULL);
+ if (seen & 2)
+ procedure (mtf_processing, NULL);
+ mtf_processing_finish (NULL);
- process_active_file (mtf_read_nonactive_records, mtf_processing,
- mtf_processing_finish, NULL);
+ dict_destroy (default_dict);
+ default_dict = mtf_master;
mtf_master = NULL;
+ vfm_source = mtf_sink->class->make_source (mtf_sink);
+ free_case_sink (mtf_sink);
mtf_free ();
return CMD_SUCCESS;
}
while (mtf_head && mtf_head->type == MTF_FILE)
- if (!mtf_processing (temp_case, NULL))
+ if (!mtf_processing (NULL, NULL))
break;
}
struct variable *v = dict_get_var (f->dict, i);
if (v->type == NUMERIC)
- compaction_case->data[v->p.mtf.master->fv].f = SYSMIS;
+ mtf_case->data[v->p.mtf.master->fv].f = SYSMIS;
else
- memset (compaction_case->data[v->p.mtf.master->fv].s, ' ',
- v->width);
+ memset (mtf_case->data[v->p.mtf.master->fv].s, ' ', v->width);
}
}
-
+
mtf_free_file (f);
}
iter = iter->next;
}
else
- {
- iter->input = temp_case->data;
- iter = iter->next;
- }
+ iter = iter->next;
}
}
/* Compare the BY variables for files A and B; return -1 if A < B, 0
if A == B, 1 if A > B. */
static inline int
-mtf_compare_BY_values (struct mtf_file *a, struct mtf_file *b)
+mtf_compare_BY_values (struct mtf_file *a, struct mtf_file *b,
+ struct ccase *c)
{
+ union value *a_input, *b_input;
int i;
-
+
+ assert ((a == NULL) + (b == NULL) + (c == NULL) <= 1);
+ a_input = a->input != NULL ? a->input : c->data;
+ b_input = b->input != NULL ? b->input : c->data;
for (i = 0; i < mtf_n_by; i++)
{
assert (a->by[i]->type == b->by[i]->type);
if (a->by[i]->type == NUMERIC)
{
- double af = a->input[a->by[i]->fv].f;
- double bf = b->input[b->by[i]->fv].f;
+ double af = a_input[a->by[i]->fv].f;
+ double bf = b_input[b->by[i]->fv].f;
if (af < bf)
return -1;
int result;
assert (a->by[i]->type == ALPHA);
- result = memcmp (a->input[a->by[i]->fv].s,
- b->input[b->by[i]->fv].s,
+ result = memcmp (a_input[a->by[i]->fv].s,
+ b_input[b->by[i]->fv].s,
a->by[i]->width);
if (result < 0)
return -1;
/* Perform one iteration of steps 3...7 above. */
static int
-mtf_processing (struct ccase *c UNUSED, void *aux UNUSED)
+mtf_processing (struct ccase *c, void *aux UNUSED)
{
/* List of files with minimum BY values. */
struct mtf_file *min_head, *min_tail;
max_head = max_tail = NULL;
for (iter = mtf_head->next; iter && iter->type == MTF_FILE;
iter = iter->next)
- switch (mtf_compare_BY_values (min_head, iter))
+ switch (mtf_compare_BY_values (min_head, iter, c))
{
case -1:
if (max_head)
advance = 0;
again:
- switch (mtf_compare_BY_values (min_head, iter))
+ switch (mtf_compare_BY_values (min_head, iter, c))
{
case -1:
if (max_head)
for (i = 0; i < dict_get_var_cnt (iter->dict); i++)
{
struct variable *v = dict_get_var (iter->dict, i);
+ union value *record;
if (mtf_seq_nums[v->p.mtf.master->index] == mtf_seq_num)
continue;
mtf_seq_nums[v->p.mtf.master->index] = mtf_seq_num;
-#if 0
- printf ("%s/%s: dest-fv=%d, src-fv=%d\n",
- fh_handle_name (iter->handle),
- v->name,
- v->p.mtf.master->fv, v->fv);
-#endif
+ record = iter->input != NULL ? iter->input : c->data;
+
+ assert (v->type == NUMERIC || v->type == ALPHA);
if (v->type == NUMERIC)
- compaction_case->data[v->p.mtf.master->fv].f
- = iter->input[v->fv].f;
+ mtf_case->data[v->p.mtf.master->fv].f = record[v->fv].f;
else
- {
- assert (v->type == ALPHA);
- memcpy (compaction_case->data[v->p.mtf.master->fv].s,
- iter->input[v->fv].s, v->width);
- }
+ memcpy (mtf_case->data[v->p.mtf.master->fv].s,
+ record[v->fv].s, v->width);
}
}
v->p.mtf.master->fv);
#endif
if (v->type == NUMERIC)
- compaction_case->data[v->p.mtf.master->fv].f = SYSMIS;
+ mtf_case->data[v->p.mtf.master->fv].f = SYSMIS;
else
- memset (compaction_case->data[v->p.mtf.master->fv].s, ' ',
+ memset (mtf_case->data[v->p.mtf.master->fv].s, ' ',
v->width);
}
}
/* 6. Write the output record. */
- process_active_file_output_case ();
+ mtf_sink->class->write (mtf_sink, mtf_case);
/* 7. Read another record from each input file FILE and TABLE
that we stored values from above. If we come to the end of
{
struct file_handle *handle = NULL;
struct dictionary *dict;
+ struct get_pgm *pgm;
int options = GTSV_OPT_NONE;
int type;
dict_destroy (default_dict);
default_dict = dict;
- vfm_source = create_case_source (&import_source_class, handle);
+ pgm = xmalloc (sizeof *pgm);
+ pgm->handle = handle;
+ pgm->case_size = dict_get_case_size (default_dict);
+ vfm_source = create_case_source (&import_source_class, default_dict, pgm);
return CMD_SUCCESS;
}
write_case(). */
static void
import_source_read (struct case_source *source,
+ struct ccase *c,
write_case_func *write_case, write_case_data wc_data)
{
- struct file_handle *handle = source->aux;
- while (pfm_read_case (handle, temp_case->data, default_dict))
+ struct get_pgm *pgm = source->aux;
+
+ while (pfm_read_case (pgm->handle, c->data, default_dict))
if (!write_case (wc_data))
break;
}
const struct case_source_class import_source_class =
{
"IMPORT",
+ NULL,
import_source_read,
get_source_destroy,
};
t->case_buf = xmalloc (sizeof *t->case_buf * t->nvar);
dict_destroy (dict);
- procedure (NULL, export_write_case_func, NULL, t);
+ procedure (export_write_case_func, t);
save_trns_free (&t->h);
return CMD_SUCCESS;
}
+/* Writes case C to the EXPORT file. */
static int
export_write_case_func (struct ccase *c, void *aux)
{