#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
{
#define GTSV_OPT_MATCH_FILES 004 /* The MATCH FILES procedure. */
#define GTSV_OPT_NONE 0
-/* The file being read by the input program. */
-static struct file_handle *get_file;
-
-/* The transformation being used by the SAVE procedure. */
-static struct save_trns *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 = &get_source;
- get_file = 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;
}
-/* Parses the SAVE (for XSAVE==0) and XSAVE (for XSAVE==1)
- commands. */
+/* SAVE or XSAVE command? */
+enum save_cmd
+ {
+ CMD_SAVE,
+ CMD_XSAVE
+ };
+
+/* Parses the SAVE and XSAVE commands. */
static int
-cmd_save_internal (int xsave)
+cmd_save_internal (enum save_cmd save_cmd)
{
struct file_handle *handle;
struct dictionary *dict;
}
/* Fill in transformation structure. */
- t = trns = xmalloc (sizeof *t);
+ t = xmalloc (sizeof *t);
t->h.proc = save_trns_proc;
t->h.free = save_trns_free;
t->f = handle;
t->case_buf = xmalloc (sizeof *t->case_buf * inf.case_size);
dict_destroy (dict);
- if (xsave == 0)
- /* SAVE. */
+ if (save_cmd == CMD_SAVE)
{
- procedure (NULL, save_write_case_func, NULL, NULL);
+ procedure (save_write_case_func, t);
save_trns_free (&t->h);
}
- else
- /* XSAVE. */
- add_transformation (&t->h);
+ else
+ {
+ assert (save_cmd == CMD_XSAVE);
+ add_transformation (&t->h);
+ }
return CMD_SUCCESS;
}
int
cmd_save (void)
{
- return cmd_save_internal (0);
+ return cmd_save_internal (CMD_SAVE);
}
/* Parses the XSAVE transformation command. */
int
cmd_xsave (void)
{
- return cmd_save_internal (1);
-}
-
-static int
-save_write_case_func (struct ccase * c, void *aux UNUSED)
-{
- save_trns_proc (&trns->h, c);
- return 1;
+ return cmd_save_internal (CMD_XSAVE);
}
-static int
-save_trns_proc (struct trns_header * t UNUSED, struct ccase * c)
+/* Writes the given C to the file specified by T. */
+static void
+do_write_case (struct save_trns *t, struct ccase *c)
{
- flt64 *p = trns->case_buf;
+ flt64 *p = t->case_buf;
int i;
- for (i = 0; i < trns->nvar; i++)
+ for (i = 0; i < t->nvar; i++)
{
- struct variable *v = trns->var[i];
+ struct variable *v = t->var[i];
if (v->type == NUMERIC)
{
double src = c->data[v->fv].f;
}
}
- sfm_write_case (trns->f, trns->case_buf, p - trns->case_buf);
+ 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)
+{
+ 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, 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);
\f
/* Clears internal state related to GET input procedure. */
static void
-get_source_destroy_source (void)
+get_source_destroy (struct case_source *source)
{
+ 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 (get_file);
+ 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 (write_case_func *write_case, write_case_data wc_data)
+get_source_read (struct case_source *source,
+ struct ccase *c,
+ write_case_func *write_case, write_case_data wc_data)
{
- while (sfm_read_case (get_file, temp_case->data, default_dict)
+ struct get_pgm *pgm = source->aux;
+
+ while (sfm_read_case (pgm->handle, c->data, default_dict)
&& write_case (wc_data))
;
- get_source_destroy_source ();
}
-struct case_stream get_source =
+const struct case_source_class get_source_class =
{
+ "GET",
NULL,
get_source_read,
- NULL,
- NULL,
- get_source_destroy_source,
- NULL,
- "GET",
+ get_source_destroy,
};
\f
/* 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);
}
if (iter->handle)
{
assert (iter->input == NULL);
- iter->input = xmalloc (sizeof *iter->input
- * dict_get_value_cnt (iter->dict));
+ iter->input = xmalloc (dict_get_case_size (iter->dict));
if (!sfm_read_case (iter->handle, iter->input, iter->dict))
mtf_delete_file_in_place (&iter);
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 = &import_source;
- get_file = 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;
}
/* Reads all the cases from the data file and passes them to
write_case(). */
static void
-import_source_read (write_case_func *write_case, write_case_data wc_data)
+import_source_read (struct case_source *source,
+ struct ccase *c,
+ write_case_func *write_case, write_case_data wc_data)
{
- while (pfm_read_case (get_file, temp_case->data, default_dict)
- && write_case (wc_data))
- ;
- get_source_destroy_source ();
+ struct get_pgm *pgm = source->aux;
+
+ while (pfm_read_case (pgm->handle, c->data, default_dict))
+ if (!write_case (wc_data))
+ break;
}
-struct case_stream import_source =
+const struct case_source_class import_source_class =
{
+ "IMPORT",
NULL,
import_source_read,
- NULL,
- NULL,
- get_source_destroy_source,
- NULL,
- "IMPORT",
+ get_source_destroy,
};
\f
static int export_write_case_func (struct ccase *c, void *);
}
/* Fill in transformation structure. */
- t = trns = xmalloc (sizeof *t);
+ t = xmalloc (sizeof *t);
t->h.proc = save_trns_proc;
t->h.free = save_trns_free;
t->f = handle;
t->case_buf = xmalloc (sizeof *t->case_buf * t->nvar);
dict_destroy (dict);
- procedure (NULL, export_write_case_func, NULL, NULL);
+ 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 UNUSED)
+export_write_case_func (struct ccase *c, void *aux)
{
- union value *p = (union value *) trns->case_buf;
+ struct save_trns *t = aux;
+ union value *p = (union value *) t->case_buf;
int i;
- for (i = 0; i < trns->nvar; i++)
+ for (i = 0; i < t->nvar; i++)
{
- struct variable *v = trns->var[i];
+ struct variable *v = t->var[i];
if (v->type == NUMERIC)
*p++ = c->data[v->fv];
(*p++).c = c->data[v->fv].s;
}
- pfm_write_case (trns->f, (union value *) trns->case_buf);
+ pfm_write_case (t->f, (union value *) t->case_buf);
return 1;
}