-Time-stamp: <2004-02-24 19:22:15 blp>
+Time-stamp: <2004-03-05 15:22:29 blp>
TODO
----
+In debug mode hash table code should verify that collisions are reasonably low.
+
Use posix_fadvise(POSIX_FADV_SEQUENTIAL) where available.
random.c should not know about set_seed.
+Tue Mar 9 22:34:17 2004 Ben Pfaff <blp@gnu.org>
+
+ * Updated.
+
Wed Feb 18 18:15:48 2004 Ben Pfaff <blp@gnu.org>
* Improve CROSSTABS description
(@samp{=}) and the identifier variable name. The variable must already
have been declared with @cmd{NUMERIC} or another command.
+@cmd{REPEATING DATA} should be the last command given within an
+@cmd{INPUT PROGRAM}. It should not be enclosed within a @cmd{LOOP}
+structure (@pxref{LOOP}). Use @cmd{DATA LIST} before, not after,
+@cmd{REPEATING DATA}.
+
@node WRITE, , REPEATING DATA, Data Input and Output
@section WRITE
@vindex WRITE
+Tue Mar 9 23:10:41 2004 Ben Pfaff <blp@gnu.org>
+
+ * format.c: (global array translate_fmt[]) Removed.
+ (translate_fmt) New function as replacement.
+ (parse_format_specifier_name) Rewrite.
+
+ * pfm-read.c: (convert_format) Use translate_fmt() instead of
+ translate_fmt[].
+
+ * sfm-read.c: (parse_format_spec) Ditto.
+
+ * postscript.c: (text) Fix handling of fonts with missing
+ ligatures.
+
+ * sort.c: (struct external_sort) Add temp_name member.
+ (destroy_external_sort) Free temp_dir, temp_name members.
+ (init_external_sort) Allocate temp_name.
+ (get_temp_file_name) Change prototype.
+ (open_temp_file) Deal with change to get_temp_file_name().
+ (close_temp_file) Ditto.
+ (remove_temp_file) Ditto.
+ (write_temp_file) Ditto.
+ (read_temp_file) Ditto.
+ (sort_sink_destroy) Removed.
+ (sort_sink_class) Change destroy member to null.
+
+Tue Mar 9 22:36:34 2004 Ben Pfaff <blp@gnu.org>
+
+ Eliminate temp_case.
+
+ * aggregate.c: (cmd_aggregate) No need to save/restore temp_case
+ anymore. Use agr_11x_finish().
+ (aggregate_single_case) Make first param const.
+ (accumulate_aggregate_info) Ditto.
+ (agr_00x_end_func) Use compaction_case, not temp_case.
+ (agr_11x_func) Break into agr_11x_read(), agr_11x_finish().
+
+ * data-list.c: (struct data_list_pgm) Add `case_size' member.
+ (cmd_data_list) Initialize case_size.
+ (read_from_data_list_fixed) Add struct ccase * param, use instead
+ of temp_case.
+ (read_from_data_list_free) Ditto.
+ (read_from_data_list_list) Ditto.
+ (read_one_case) Rename data_list_trns_proc(), all references
+ updated. Add argument in calling above functions. Use c
+ argument instead of temp_case.
+ (destroy_dls) Rename data_list_trns_free(), all references
+ updated.
+
+ * expr-evl.c: (expr_evaluate) Make second parameter const.
+
+ * file-type.c: (struct file_type_pgm) Add `case_size' member.
+ (cmd_end_file_type) Initialize `case_size'.
+ (file_type_source_read) Add struct ccase * parameter. Use instead
+ of temp_case.
+
+ * flip.c: Rewritten.
+
+ * get.c: (struct get_pgm) New structure to keep track of
+ case_size.
+ (cmd_get) Initialize case_size.
+ (cmd_import) Ditto.
+ (get_source) Deal with struct get_pgm.
+ (get_source_read) Add struct ccase * parameter, use instead of
+ temp_case.
+ (import_source_read) Ditto.
+
+ * get.c: Use a null pointer instead of temp_case to represent the
+ "current case" in a struct mtf_file's input member.
+ (mtf_processing_finish) Pass null to mtf_processing(), not
+ temp_case.
+ (mtf_read_nonactive_records) Don't set iter->input to temp_case.
+ (mtf_compare_BY_values) Add extra arg, use instead of null input
+ members.
+ (mtf_processing) Use c parameter instead of temp_case. Pass
+ compaction_case to process_active_file_output_case().
+
+ * glob.c: (global variable temp_case) Removed.
+
+ * inpt-pgm.c: (struct input_program_pgm) Add `case_size' member.
+ (cmd_input_program) Initialize case_size. Set
+ vfm_source->value_cnt.
+ (init_case) Add struct ccase * parameter, use instead of
+ temp_case.
+ (clear_case) Ditto.
+ (input_program_source_read) Ditto.
+
+ * matrix-data.c: (matrix_data_read_without_rowtype) Ditto.
+ (dump_cell_content) Ditto.
+ (nr_output_data) Ditto.
+ (read_matrices_without_rowtype) Ditto.
+ (matrix_data_read_with_rowtype) Ditto.
+ (wr_read_splits) Ditto.
+ (wr_output_data) Ditto.
+
+ * sort.h: (struct sort_cases_pgm) New member `case_size'.
+
+ * sort.c: (sort_cases) Initialize scp->case_size.
+ (struct external_sort) Remove `case_size' member.
+ (write_initial_runs) Only call vfm_sink->class_destroy if
+ non-null.
+ (struct sort_source_aux) New structure.
+ (sort_source_read_helper) New function.
+ (sort_source_read) Use sort_source_read_helper().
+ (read_sort_output) Change interface to be more reasonable.
+ (read_internal_sort_output) Ditto.
+ (read_external_sort_output) Ditto.
+
+ * vars-prs.c: (dict_class_to_name) Pass return value through
+ gettext.
+
+ * vfm.c: (struct procedure_aux_data) Add `trns_case' member.
+ (procedure) Initialize trns_case.
+ (procedure) Pass trns_case to vfm_source->class->read().
+ Free trns_case.
+ (process_active_file) Start using struct procedure_aux_data.
+ (process_active_file_write_case) Pass trns_case to
+ transformations, lag_case(), clear_case().
+ (process_active_file_output_case) Add struct ccase * parameter.
+ (create_trns_case) New function.
+ (make_temp_case) Removed.
+ (vector_initialization) Removed.
+ (close_active_file) Only call make_source if non-null, otherwise
+ set vfm_source to null pointer. Don't free temp_case.
+ (disk_source_read) Add struct ccase * parameter, use instead of
+ temp_case.
+ (memory_source_read) Ditto.
+ (lag_case) Add const struct ccase * member.
+ (procedure_write_case) Use trns_case instead of temp_case.
+ (clear_case) Add struct ccase * member, use instead of temp_case.
+ (exclude_this_case) Ditto.
+ (create_case_source) Add struct dictionary * parameter, use to
+ initialize source->value_cnt.
+
+ * vfm.h: (struct case_source) Add `value_cnt' member.
+ (struct case_source_class) Add struct ccase * parameter to `read'
+ member function pointer.
+ (struct case_sink_class) Make struct ccase * parameter const in
+ `write' member function pointer.
+
Wed Mar 3 20:44:37 2004 Ben Pfaff <blp@gnu.org>
Fix a lot of "possibly uninitialized variable" warnings. Some of
/* Prototypes. */
static int parse_aggregate_functions (void);
static void free_aggregate_functions (void);
-static int aggregate_single_case (struct ccase *input, struct ccase *output);
+static int aggregate_single_case (const struct ccase *input,
+ struct ccase *output);
static int create_sysfile (void);
static trns_proc_func agr_00x_trns_proc, agr_10x_trns_proc;
static trns_free_func agr_10x_trns_free;
static void agr_00x_end_func (void *aux);
static void agr_10x_end_func (void *);
-static int agr_11x_func (write_case_data);
+static read_sort_output_func agr_11x_read;
+static void agr_11x_finish (void);
#if DEBUGGING
static void debug_print (int flags);
}
case 6:
- case 7:
+ case 7:
sort_cases (sort, 1);
if (!create_sysfile ())
goto lossage;
- read_sort_output (sort, agr_11x_func, NULL);
-
- {
- struct ccase *save_temp_case = temp_case;
- temp_case = NULL;
- agr_11x_func (NULL);
- temp_case = save_temp_case;
- }
-
+ read_sort_output (sort, agr_11x_read, NULL);
+ agr_11x_finish ();
+
break;
default:
\f
/* Execution. */
-static void accumulate_aggregate_info (struct ccase *input);
+static void accumulate_aggregate_info (const struct ccase *input);
static void dump_aggregate_info (struct ccase *output);
/* Processes a single case INPUT for aggregation. If output is
/* The code in this function has an eerie similarity to
vfm.c:SPLIT_FILE_procfunc()... */
static int
-aggregate_single_case (struct ccase *input, struct ccase *output)
+aggregate_single_case (const struct ccase *input, struct ccase *output)
{
/* The first case always begins a new break group. We also need to
preserve the values of the case for later comparison. */
/* Accumulates aggregation data from the case INPUT. */
static void
-accumulate_aggregate_info (struct ccase *input)
+accumulate_aggregate_info (const struct ccase *input)
{
struct agr_var *iter;
double weight;
for (iter = agr_first; iter; iter = iter->next)
if (iter->src)
{
- union value *v = &input->data[iter->src->fv];
+ const union value *v = &input->data[iter->src->fv];
if ((!iter->include_missing && is_missing (v, iter->src))
|| (iter->include_missing && iter->src->type == NUMERIC
/* Ensure that info for the last break group gets written to the
active file. */
dump_aggregate_info (compaction_case);
- vfm_sink->class->write (vfm_sink, temp_case);
+ vfm_sink->class->write (vfm_sink, compaction_case);
}
/* Transform the aggregate case buf_1xx, in internal format, to system
write_case_to_sfm ();
}
-/* When called with temp_case non-NULL (the normal case), runs the
- case through the aggregater and outputs it to the system file if
- appropriate. If temp_case is NULL, finishes up writing the last
- case if necessary. */
+/* Runs case C through the aggregater and outputs it to the
+ system file if appropriate. */
static int
-agr_11x_func (write_case_data wc_data UNUSED)
+agr_11x_read (const struct ccase *c, void *aux UNUSED)
{
- if (temp_case != NULL)
- {
- int code = aggregate_single_case (temp_case, buf_1xx);
+ int code = aggregate_single_case (c, buf_1xx);
- assert (code == -2 || code == -1);
- if (code == -1)
- write_case_to_sfm ();
- }
- else
+ assert (code == -2 || code == -1);
+ if (code == -1)
+ write_case_to_sfm ();
+
+ return 1;
+}
+
+/* Finishes up writing the last case if necessary. */
+static void
+agr_11x_finish (void)
+{
+ if (case_count)
{
- if (case_count)
- {
- dump_aggregate_info (buf_1xx);
- write_case_to_sfm ();
- }
- fh_close_handle (outfile);
+ dump_aggregate_info (buf_1xx);
+ write_case_to_sfm ();
}
- return 1;
+ fh_close_handle (outfile);
}
\f
/* Debugging. */
\f
/* Transformation functions. */
+/* Handle COMPUTE or IF with numeric target variable. */
static int
compute_num (struct trns_header *compute_, struct ccase *c,
int case_num)
return -1;
}
+/* Handle COMPUTE or IF with numeric vector element target
+ variable. */
static int
compute_num_vec (struct trns_header *compute_, struct ccase *c,
int case_num)
return -1;
}
+/* Handle COMPUTE or IF with string target variable. */
static int
compute_str (struct trns_header *compute_, struct ccase *c,
int case_num)
return -1;
}
+/* Handle COMPUTE or IF with string vector element target
+ variable. */
static int
compute_str_vec (struct trns_header *compute_, struct ccase *c,
int case_num)
expr_free (compute->rvalue);
}
\f
+/* COMPUTE or IF target variable or vector element. */
struct lvalue
{
char var_name[9]; /* Destination variable name, or "". */
struct expression *element; /* Destination vector element, or NULL. */
};
+/* Parses the target variable or vector elector into a new
+ `struct lvalue', which is returned. */
static struct lvalue *
lvalue_parse (void)
{
return NULL;
}
+/* Returns the type (NUMERIC or ALPHA) of the target variable or
+ vector in LVALUE. */
static int
lvalue_get_type (const struct lvalue *lvalue)
{
return lvalue->vector->var[0]->type;
}
+/* Returns nonzero if LVALUE has a vector as its target. */
static int
lvalue_is_vector (const struct lvalue *lvalue)
{
return lvalue->vector != NULL;
}
+/* Finalizes making LVALUE the target of COMPUTE, by creating the
+ target variable if necessary and setting fields in COMPUTE. */
static void
lvalue_finalize (struct lvalue *lvalue,
struct compute_trns *compute)
compute->fv = compute->variable->fv;
compute->width = compute->variable->width;
-
-
/* Goofy behavior, but compatible: Turn off LEAVE. */
if (dict_class_from_id (compute->variable->name) != DC_SCRATCH)
compute->variable->reinit = 1;
lvalue_destroy (lvalue);
}
+/* Destroys LVALUE. */
static void
lvalue_destroy (struct lvalue *lvalue)
{
struct data_list_pgm
{
struct trns_header h;
+
struct dls_var_spec *first, *last; /* Variable parsing specifications. */
struct file_handle *handle; /* Input file, never NULL. */
- /* Do not reorder preceding fields. */
int type; /* A DLS_* constant. */
struct variable *end; /* Variable specified on END subcommand. */
int eof; /* End of file encountered. */
int nrec; /* Number of records. */
+ size_t case_size; /* Case size in bytes. */
};
static int parse_fixed (struct data_list_pgm *);
const struct file_handle *handle, int nrec);
static void dump_free_table (const struct data_list_pgm *);
static void destroy_dls_var_spec (struct dls_var_spec *);
-static trns_free_func destroy_dls;
-static trns_proc_func read_one_case;
+static trns_free_func data_list_trns_free;
+static trns_proc_func data_list_trns_proc;
/* Message title for REPEATING DATA. */
#define RPD_ERR "REPEATING DATA: "
}
}
+ dls->case_size = dict_get_case_size (default_dict);
default_handle = dls->handle;
if (dls->type == -1)
{
struct data_list_pgm *new_pgm;
- dls->h.proc = read_one_case;
- dls->h.free = destroy_dls;
+ dls->h.proc = data_list_trns_proc;
+ dls->h.free = data_list_trns_free;
new_pgm = xmalloc (sizeof *new_pgm);
memcpy (new_pgm, &dls, sizeof *new_pgm);
add_transformation (&new_pgm->h);
}
else
- vfm_source = create_case_source (&data_list_source_class, dls);
+ vfm_source = create_case_source (&data_list_source_class,
+ default_dict, dls);
return CMD_SUCCESS;
return CMD_FAILURE;
}
+/* Adds SPEC to the linked list with head at FIRST and tail at
+ LAST. */
static void
append_var_spec (struct dls_var_spec **first, struct dls_var_spec **last,
struct dls_var_spec *spec)
struct fmt_list *down;
};
-/* Used as "local" variables among the fixed-format parsing funcs. If
- it were guaranteed that PSPP were going to be compiled by gcc,
- I'd make all these functions a single set of nested functions. */
+/* State of parsing DATA LIST. */
struct fixed_parsing_state
{
char **name; /* Variable names. */
struct dls_var_spec **,
struct dls_var_spec **);
+/* Parses all the variable specifications for DATA LIST FIXED,
+ storing them into DLS. Returns nonzero if successful. */
static int
parse_fixed (struct data_list_pgm *dls)
{
return 0;
}
+/* Parses a variable specification in the form 1-10 (A) based on
+ FX and adds specifications to the linked list with head at
+ FIRST and tail at LAST. */
static int
fixed_parse_compatible (struct fixed_parsing_state *fx,
struct dls_var_spec **first, struct dls_var_spec **last)
return 1;
}
-/* Destroy a format list and, optionally, all its sublists. */
+/* Destroy format list F and, if RECURSE is nonzero, all its
+ sublists. */
static void
destroy_fmt_list (struct fmt_list *f, int recurse)
{
}
/* Takes a hierarchically structured fmt_list F as constructed by
- fixed_parse_fortran(), and flattens it into a linear list of
- dls_var_spec's. NAME_IDX is used to take values from the list
- of names in FX; it should initially point to a value of 0. */
+ fixed_parse_fortran(), and flattens it, adding the variable
+ specifications to the linked list with head FIRST and tail
+ LAST. NAME_IDX is used to take values from the list of names
+ in FX; it should initially point to a value of 0. */
static int
dump_fmt_list (struct fixed_parsing_state *fx, struct fmt_list *f,
struct dls_var_spec **first, struct dls_var_spec **last,
return 1;
}
-/* Recursively parses a FORTRAN-like format specification. LEVEL
- is the level of recursion, starting from 0. Returns the
- parsed specification if successful, or a null pointer on
- failure. */
+/* Recursively parses a FORTRAN-like format specification into
+ the linked list with head FIRST and tail TAIL. LEVEL is the
+ level of recursion, starting from 0. Returns the parsed
+ specification if successful, or a null pointer on failure. */
static struct fmt_list *
fixed_parse_fortran_internal (struct fixed_parsing_state *fx,
struct dls_var_spec **first,
return NULL;
}
-/* Parses a FORTRAN-like format specification. Returns nonzero
- if successful. */
+/* Parses a FORTRAN-like format specification into the linked
+ list with head FIRST and tail LAST. Returns nonzero if
+ successful. */
static int
fixed_parse_fortran (struct fixed_parsing_state *fx,
struct dls_var_spec **first, struct dls_var_spec **last)
\f
/* Free-format parsing. */
+/* Parses variable specifications for DATA LIST FREE and adds
+ them to the linked list with head FIRST and tail LAST.
+ Returns nonzero only if successful. */
static int
parse_free (struct dls_var_spec **first, struct dls_var_spec **last)
{
}
}
-typedef int data_list_read_func (const struct data_list_pgm *);
+typedef int data_list_read_func (const struct data_list_pgm *, struct ccase *);
static data_list_read_func read_from_data_list_fixed;
static data_list_read_func read_from_data_list_free;
static data_list_read_func read_from_data_list_list;
{
case DLS_FIXED:
return read_from_data_list_fixed;
- break;
case DLS_FREE:
return read_from_data_list_free;
- break;
case DLS_LIST:
return read_from_data_list_list;
- break;
default:
assert (0);
}
}
-/* Reads a case from the data file and parses it according to
- fixed-format syntax rules. Returns -1 on success, -2 at end
- of file. */
+/* Reads a case from the data file into C, parsing it according
+ to fixed-format syntax rules in DLS. Returns -1 on success,
+ -2 at end of file. */
static int
-read_from_data_list_fixed (const struct data_list_pgm *dls)
+read_from_data_list_fixed (const struct data_list_pgm *dls,
+ struct ccase *c)
{
struct dls_var_spec *var_spec = dls->first;
int i;
struct data_in di;
data_in_finite_line (&di, line, len, var_spec->fc, var_spec->lc);
- di.v = &temp_case->data[var_spec->fv];
+ di.v = &c->data[var_spec->fv];
di.flags = 0;
di.f1 = var_spec->fc;
di.format = var_spec->input;
return -1;
}
-/* Reads a case from the data file and parses it according to
- free-format syntax rules. Returns -1 on success, -2 at end of
- file. */
+/* Reads a case from the data file into C, parsing it according
+ to free-format syntax rules in DLS. Returns -1 on success,
+ -2 at end of file. */
static int
-read_from_data_list_free (const struct data_list_pgm *dls)
+read_from_data_list_free (const struct data_list_pgm *dls,
+ struct ccase *c)
{
struct dls_var_spec *var_spec;
char *field;
di.s = field;
di.e = field + len;
- di.v = &temp_case->data[var_spec->fv];
+ di.v = &c->data[var_spec->fv];
di.flags = 0;
di.f1 = column;
di.format = var_spec->input;
list-format syntax rules. Returns -1 on success, -2 at end of
file. */
static int
-read_from_data_list_list (const struct data_list_pgm *dls)
+read_from_data_list_list (const struct data_list_pgm *dls,
+ struct ccase *c)
{
struct dls_var_spec *var_spec;
char *field;
{
int width = get_format_var_width (&var_spec->input);
if (width == 0)
- temp_case->data[var_spec->fv].f = SYSMIS;
+ c->data[var_spec->fv].f = SYSMIS;
else
- memset (temp_case->data[var_spec->fv].s, ' ', width);
+ memset (c->data[var_spec->fv].s, ' ', width);
}
break;
}
di.s = field;
di.e = field + len;
- di.v = &temp_case->data[var_spec->fv];
+ di.v = &c->data[var_spec->fv];
di.flags = 0;
di.f1 = column;
di.format = var_spec->input;
/* Destroys DATA LIST transformation PGM. */
static void
-destroy_dls (struct trns_header *pgm)
+data_list_trns_free (struct trns_header *pgm)
{
struct data_list_pgm *dls = (struct data_list_pgm *) pgm;
destroy_dls_var_spec (dls->first);
free (pgm);
}
-/* Note that since this is exclusively an input program, C is
- guaranteed to be temp_case. */
+/* Handle DATA LIST transformation T, parsing data into C. */
static int
-read_one_case (struct trns_header *t, struct ccase *c UNUSED,
- int case_num UNUSED)
+data_list_trns_proc (struct trns_header *t, struct ccase *c,
+ int case_num UNUSED)
{
struct data_list_pgm *dls = (struct data_list_pgm *) t;
data_list_read_func *read_func;
dfm_push (dls->handle);
read_func = get_data_list_read_func (dls);
- retval = read_func (dls);
+ retval = read_func (dls, c);
/* Handle end of file. */
if (retval == -2)
{
if (retval == -2)
{
- temp_case->data[dls->end->fv].f = 1.0;
+ c->data[dls->end->fv].f = 1.0;
retval = -1;
}
else
- temp_case->data[dls->end->fv].f = 0.0;
+ c->data[dls->end->fv].f = 0.0;
}
dfm_pop (dls->handle);
write_case(). */
static void
data_list_source_read (struct case_source *source,
+ struct ccase *c,
write_case_func *write_case, write_case_data wc_data)
{
struct data_list_pgm *dls = source->aux;
data_list_read_func *read_func = get_data_list_read_func (dls);
dfm_push (dls->handle);
- while (read_func (dls) != -2)
+ while (read_func (dls, c) != -2)
if (!write_case (wc_data))
break;
dfm_pop (dls->handle);
static void
data_list_source_destroy (struct case_source *source)
{
- destroy_dls (source->aux);
+ data_list_trns_free (source->aux);
}
const struct case_source_class data_list_source_class =
return CMD_FAILURE;
}
-/* Because of the way that DATA LIST is structured, it's not trivial
- to determine what input format is associated with a given variable.
- This function finds the input format specification for variable V
- and puts it in SPEC. */
+/* Finds the input format specification for variable V and puts
+ it in SPEC. Because of the way that DATA LIST is structured,
+ this is nontrivial. */
static void
find_variable_input_spec (struct variable *v, struct fmt_spec *spec)
{
{
struct data_list_pgm *pgm = (struct data_list_pgm *) t_trns[i];
- if (pgm->h.proc == read_one_case)
+ if (pgm->h.proc == data_list_trns_proc)
{
struct dls_var_spec *iter;
return 1;
}
-/* Parses data specifications for repeating data groups. Taken from
- parse_fixed(). Returns nonzero only if successful. */
+/* Parses data specifications for repeating data groups, adding
+ them to the linked list with head FIRST and tail LAST.
+ Returns nonzero only if successful. */
static int
parse_repeating_data (struct dls_var_spec **first, struct dls_var_spec **last)
{
return occurrences;
}
-/* Analogous to read_one_case; reads one set of repetitions of the
- elements in the REPEATING DATA structure. Returns -1 on success,
- -2 on end of file or on failure. */
+/* Reads one set of repetitions of the elements in the REPEATING
+ DATA structure. Returns -1 on success, -2 on end of file or
+ on failure. */
int
repeating_data_trns_proc (struct trns_header *trns, struct ccase *c,
int case_num UNUSED)
return -3;
}
+/* Frees a REPEATING DATA transformation. */
void
repeating_data_trns_free (struct trns_header *rpd_)
{
free (rpd->id_value);
}
-/* This is a kluge. It is only here until I have more time
- tocome up with something better. It lets
- repeating_data_trns_proc() know how to write the cases that it
- composes. */
+/* Lets repeating_data_trns_proc() know how to write the cases
+ that it composes. Not elegant. */
void
repeating_data_set_write_case (struct trns_header *trns,
write_case_func *write_case,
#include "vfmP.h"
double
-expr_evaluate (struct expression *e, struct ccase *c, int case_num,
+expr_evaluate (struct expression *e, const struct ccase *c, int case_num,
union value *v)
{
unsigned char *op = e->op;
union value;
struct expression *expr_parse (int flags);
-double expr_evaluate (struct expression *, struct ccase *, int case_num,
+double expr_evaluate (struct expression *, const struct ccase *, int case_num,
union value *);
void expr_free (struct expression *);
DATA LIST. */
struct record_type *recs_head; /* List of record types. */
struct record_type *recs_tail; /* Last in list of record types. */
+ size_t case_size; /* Case size in bytes. */
};
static int parse_col_spec (struct col_spec *, const char *);
default_handle = fty->handle;
- vfm_source = create_case_source (&file_type_source_class, fty);
create_col_var (&fty->record);
if (fty->case_sbc.name[0])
create_col_var (&fty->case_sbc);
+ vfm_source = create_case_source (&file_type_source_class, default_dict, fty);
return CMD_SUCCESS;
return CMD_FAILURE;
}
fty = vfm_source->aux;
+ fty->case_size = dict_get_case_size (default_dict);
lex_match_id ("TYPE");
static void read_from_file_type_grouped(void);
static void read_from_file_type_nested(void); */
-/* Reads any number of cases into temp_case and calls write_case() for
- each one. Compare data-list.c:read_from_data_list. */
+/* Reads any number of cases into case C and calls write_case()
+ for each one. Compare data-list.c:read_from_data_list. */
static void
file_type_source_read (struct case_source *source,
+ struct ccase *c,
write_case_func *write_case UNUSED,
write_case_data wc_data UNUSED)
{
{
struct data_in di;
- v.c = temp_case->data[fty->record.v->fv].s;
+ v.c = c->data[fty->record.v->fv].s;
data_in_finite_line (&di, line, len,
fty->record.fc, fty->record.fc + fty->record.nc);
di.format = format;
data_in (&di);
- memcpy (&temp_case->data[fty->record.v->fv].f, &v.f, sizeof v.f);
+ memcpy (&c->data[fty->record.v->fv].f, &v.f, sizeof v.f);
for (iter = fty->recs_head; iter; iter = iter->next)
{
if (iter->flags & RCT_OTHER)
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA. */
-/* FIXME: does this work with long string variables? */
-
#include <config.h>
#include <assert.h>
#include <ctype.h>
struct flip_pgm
{
struct variable **var; /* Variables to transpose. */
- int var_cnt; /* Number of variables. */
- struct variable *newnames; /* Variable containing new variable names. */
- struct varname *new_names_head, *new_names_tail;
- /* New variable names. */
- int case_count; /* Number of cases. */
+ int var_cnt; /* Number of elements in `var'. */
+ int case_cnt; /* Pre-flip case count. */
+ size_t case_size; /* Post-flip bytes per case. */
+
+ struct variable *new_names; /* Variable containing new variable names. */
+ struct varname *new_names_head; /* First new variable. */
+ struct varname *new_names_tail; /* Last new variable. */
+ FILE *file; /* Temporary file containing data. */
};
-static void destroy_flip_pgm (struct flip_pgm *flip);
+static void destroy_flip_pgm (struct flip_pgm *);
static struct case_sink *flip_sink_create (struct flip_pgm *);
+static struct case_source *flip_source_create (struct flip_pgm *);
+static void flip_file (struct flip_pgm *);
+static int build_dictionary (struct flip_pgm *);
+
static const struct case_source_class flip_source_class;
-static int build_dictionary (struct flip_pgm *flip);
+static const struct case_sink_class flip_sink_class;
/* Parses and executes FLIP. */
int
flip = xmalloc (sizeof *flip);
flip->var = NULL;
flip->var_cnt = 0;
- flip->newnames = NULL;
- flip->new_names_head = flip->new_names_tail = NULL;
- flip->case_count = 0;
+ flip->case_cnt = 0;
+ flip->new_names = NULL;
+ flip->new_names_head = NULL;
+ flip->new_names_tail = NULL;
+ flip->file = NULL;
lex_match_id ("FLIP");
lex_match ('/');
if (lex_match_id ("NEWNAMES"))
{
lex_match ('=');
- flip->newnames = parse_variable ();
- if (!flip->newnames)
+ flip->new_names = parse_variable ();
+ if (!flip->new_names)
goto error;
}
else
- flip->newnames = dict_lookup_var (default_dict, "CASE_LBL");
+ flip->new_names = dict_lookup_var (default_dict, "CASE_LBL");
- if (flip->newnames)
+ if (flip->new_names)
{
int i;
for (i = 0; i < flip->var_cnt; i++)
- if (flip->var[i] == flip->newnames)
+ if (flip->var[i] == flip->new_names)
{
memmove (&flip->var[i], &flip->var[i + 1], sizeof *flip->var * (flip->var_cnt - i - 1));
flip->var_cnt--;
}
}
- flip->case_count = 0;
+ /* Read the active file into a flip_sink. */
+ flip->case_cnt = 0;
temp_trns = temporary = 0;
vfm_sink = flip_sink_create (flip);
flip->new_names_tail = NULL;
procedure (NULL, NULL, NULL, NULL);
+ /* Flip the data we read. */
+ flip_file (flip);
+
+ /* Flip the dictionary. */
dict_clear (default_dict);
if (!build_dictionary (flip))
{
discard_variables ();
goto error;
}
+ flip->case_size = dict_get_case_size (default_dict);
+
+ /* Set up flipped data for reading. */
+ vfm_source = flip_source_create (flip);
return lex_end_of_command ();
return CMD_FAILURE;
}
+/* Destroys FLIP. */
static void
destroy_flip_pgm (struct flip_pgm *flip)
{
next = iter->next;
free (iter);
}
+ if (flip->file != NULL)
+ fclose (flip->file);
free (flip);
}
{
int i;
- if (flip->case_count > 99999)
+ if (flip->case_cnt > 99999)
{
msg (SE, _("Cannot create more than 99999 variable names."));
return 0;
}
- for (i = 0; i < flip->case_count; i++)
+ for (i = 0; i < flip->case_cnt; i++)
{
struct variable *v;
char s[9];
struct flip_sink_info
{
struct flip_pgm *flip; /* FLIP program. */
- int internal; /* Internal or external flip. */
- char *old_names; /* Old variable names. */
- unsigned long case_cnt; /* Number of cases. */
- FILE *file; /* Temporary file. */
- };
-
-/* Source: Cases after transposition. */
-struct flip_source_info
- {
- struct flip_pgm *flip; /* FLIP program. */
- char *old_names; /* Old variable names. */
- unsigned long case_cnt; /* Number of cases. */
- FILE *file; /* Temporary file. */
+ union value *output_buf; /* Case output buffer. */
};
-static const struct case_sink_class flip_sink_class;
-
-static FILE *flip_file (struct flip_sink_info *info);
-
-/* Creates a flip sink based on FLIP, of which it takes
- ownership. */
+/* Creates a flip sink based on FLIP. */
static struct case_sink *
flip_sink_create (struct flip_pgm *flip)
{
struct flip_sink_info *info = xmalloc (sizeof *info);
+ int i;
info->flip = flip;
- info->case_cnt = 0;
-
- {
- size_t n = flip->var_cnt;
- char *p;
- int i;
-
- for (i = 0; i < flip->var_cnt; i++)
- n += strlen (flip->var[i]->name);
- p = info->old_names = xmalloc (n);
- for (i = 0; i < flip->var_cnt; i++)
- p = stpcpy (p, flip->var[i]->name) + 1;
- }
+ info->output_buf = xmalloc (sizeof *info->output_buf * flip->var_cnt);
- return create_case_sink (&flip_sink_class, info);
-}
+ flip->file = tmpfile ();
+ if (!flip->file)
+ msg (FE, _("Could not create temporary file for FLIP."));
-/* Open the FLIP sink. */
-static void
-flip_sink_open (struct case_sink *sink)
-{
- struct flip_sink_info *info = sink->aux;
+ /* Write variable names as first case. */
+ for (i = 0; i < flip->var_cnt; i++)
+ st_bare_pad_copy (info->output_buf[i].s, flip->var[i]->name, 8);
+ if (fwrite (info->output_buf, sizeof *info->output_buf,
+ flip->var_cnt, flip->file) != (size_t) flip->var_cnt)
+ msg (FE, _("Error writing FLIP file: %s."), strerror (errno));
- info->file = tmpfile ();
- if (!info->file)
- msg (FE, _("Could not create temporary file for FLIP."));
+ flip->case_cnt = 1;
+
+ return create_case_sink (&flip_sink_class, info);
}
/* Writes case C to the FLIP sink. */
static void
-flip_sink_write (struct case_sink *sink, struct ccase *c)
+flip_sink_write (struct case_sink *sink, const struct ccase *c)
{
struct flip_sink_info *info = sink->aux;
struct flip_pgm *flip = info->flip;
+ int i;
- info->case_cnt++;
+ flip->case_cnt++;
- if (flip->newnames)
+ if (flip->new_names != NULL)
{
struct varname *v = xmalloc (sizeof (struct varname));
v->next = NULL;
- if (flip->newnames->type == NUMERIC)
+ if (flip->new_names->type == NUMERIC)
{
- double f = c->data[flip->newnames->fv].f;
+ double f = c->data[flip->new_names->fv].f;
if (f == SYSMIS)
strcpy (v->name, "VSYSMIS");
}
else
{
- int width = min (flip->newnames->width, 8);
- memcpy (v->name, c->data[flip->newnames->fv].s, width);
+ int width = min (flip->new_names->width, 8);
+ memcpy (v->name, c->data[flip->new_names->fv].s, width);
v->name[width] = 0;
}
flip->new_names_tail->next = v;
flip->new_names_tail = v;
}
- else
- flip->case_count++;
-
/* Write to external file. */
- {
- double *d = local_alloc (sizeof *d * flip->var_cnt);
- int i;
-
- for (i = 0; i < flip->var_cnt; i++)
- if (flip->var[i]->type == NUMERIC)
- d[i] = c->data[flip->var[i]->fv].f;
- else
- d[i] = SYSMIS;
+ for (i = 0; i < flip->var_cnt; i++)
+ if (flip->var[i]->type == NUMERIC)
+ info->output_buf[i].f = c->data[flip->var[i]->fv].f;
+ else
+ info->output_buf[i].f = SYSMIS;
- if (fwrite (d, sizeof *d, flip->var_cnt, info->file) != (size_t) flip->var_cnt)
- msg (FE, _("Error writing FLIP file: %s."),
- strerror (errno));
-
- local_free (d);
- }
+ if (fwrite (info->output_buf, sizeof *info->output_buf,
+ flip->var_cnt, flip->file) != (size_t) flip->var_cnt)
+ msg (FE, _("Error writing FLIP file: %s."), strerror (errno));
}
-/* Destroy sink's internal data. */
+/* Transposes the external file into a new file. */
static void
-flip_sink_destroy (struct case_sink *sink)
+flip_file (struct flip_pgm *flip)
{
- struct flip_sink_info *info = sink->aux;
-
- free (info->old_names);
- destroy_flip_pgm (info->flip);
- free (info);
-}
-
-/* Convert the FLIP sink into a source. */
-static struct case_source *
-flip_sink_make_source (struct case_sink *sink)
-{
- struct flip_sink_info *sink_info = sink->aux;
- struct flip_source_info *source_info;
-
- source_info = xmalloc (sizeof *source_info);
- source_info->flip = sink_info->flip;
- source_info->old_names = sink_info->old_names;
- source_info->case_cnt = sink_info->case_cnt;
- source_info->file = flip_file (sink_info);
- fclose (sink_info->file);
-
- free (sink_info);
-
- return create_case_source (&flip_source_class, source_info);
-}
-
-/* Transposes the external file into a new file and returns a
- pointer to the transposed file. */
-static FILE *
-flip_file (struct flip_sink_info *info)
-{
- struct flip_pgm *flip = info->flip;
size_t case_bytes;
size_t case_capacity;
size_t case_idx;
/* Allocate memory for many cases. */
case_bytes = flip->var_cnt * sizeof *input_buf;
case_capacity = set_max_workspace / case_bytes;
- if (case_capacity > info->case_cnt)
- case_capacity = info->case_cnt;
+ if (case_capacity > flip->case_cnt * 2)
+ case_capacity = flip->case_cnt * 2;
if (case_capacity < 2)
case_capacity = 2;
for (;;)
case_capacity /= 2;
output_buf = input_buf + flip->var_cnt * case_capacity;
- input_file = info->file;
+ input_file = flip->file;
if (fseek (input_file, 0, SEEK_SET) != 0)
msg (FE, _("Error rewinding FLIP file: %s."), strerror (errno));
if (output_file == NULL)
msg (FE, _("Error creating FLIP source file."));
- for (case_idx = 0; case_idx < info->case_cnt; )
+ for (case_idx = 0; case_idx < flip->case_cnt; )
{
- unsigned long read_cases = min (info->case_cnt - case_idx,
+ unsigned long read_cases = min (flip->case_cnt - case_idx,
case_capacity);
int i;
output_buf[j] = input_buf[i + j * flip->var_cnt];
if (fseek (output_file,
- sizeof *input_buf * (case_idx + i * info->case_cnt),
+ sizeof *input_buf * (case_idx + i * flip->case_cnt),
SEEK_SET) != 0)
msg (FE, _("Error seeking FLIP source file: %s."),
strerror (errno));
case_idx += read_cases;
}
+ fclose (input_file);
+ free (input_buf);
+
if (fseek (output_file, 0, SEEK_SET) != 0)
msg (FE, _("Error rewind FLIP source file: %s."), strerror (errno));
+ flip->file = output_file;
+}
- free (input_buf);
- return output_file;
+/* Destroy sink's internal data. */
+static void
+flip_sink_destroy (struct case_sink *sink)
+{
+ struct flip_sink_info *info = sink->aux;
+
+ free (info->output_buf);
+ free (info);
}
/* FLIP sink class. */
static const struct case_sink_class flip_sink_class =
{
"FLIP",
- flip_sink_open,
+ NULL,
flip_sink_write,
flip_sink_destroy,
- flip_sink_make_source,
+ NULL,
};
-/* Reads the FLIP stream and passes it to WRITE_CASE(). */
+/* Creates and returns a FLIP source based on PGM,
+ which should have already been used as a sink. */
+static struct case_source *
+flip_source_create (struct flip_pgm *pgm)
+{
+ return create_case_source (&flip_source_class, default_dict, pgm);
+}
+
+/* Reads the FLIP stream. Copies each case into C and calls
+ WRITE_CASE passing WC_DATA. */
static void
flip_source_read (struct case_source *source,
+ struct ccase *c,
write_case_func *write_case, write_case_data wc_data)
{
- struct flip_source_info *info = source->aux;
- struct flip_pgm *flip = info->flip;
+ struct flip_pgm *flip = source->aux;
int i;
- char *p = info->old_names;
-
+
for (i = 0; i < flip->var_cnt; i++)
{
- st_bare_pad_copy (temp_case->data[0].s, p, 8);
- p = strchr (p, 0) + 1;
-
- if (fread (&temp_case->data[1], sizeof (double), info->case_cnt,
- info->file) != info->case_cnt)
- msg (FE, _("Error reading FLIP source file: %s."),
- strerror (errno));
+ if (fread (c->data, sizeof *c->data, flip->case_cnt,
+ flip->file) != flip->case_cnt)
+ {
+ if (ferror (flip->file))
+ msg (SE, _("Error reading FLIP temporary file: %s."),
+ strerror (errno));
+ else if (feof (flip->file))
+ msg (SE, _("Unexpected end of file reading FLIP temporary file."));
+ else
+ assert (0);
+ break;
+ }
if (!write_case (wc_data))
- return;
+ break;
}
}
-/* Destroy source's internal data. */
+/* Destroy internal data in SOURCE. */
static void
flip_source_destroy (struct case_source *source)
{
- struct flip_source_info *info = source->aux;
+ struct flip_pgm *flip = source->aux;
- destroy_flip_pgm (info->flip);
- free (info->old_names);
- fclose (info->file);
- free (info);
+ destroy_flip_pgm (flip);
}
static const struct case_source_class flip_source_class =
{"", -1, -1, -1, -1, -1, 0000, -1, -1},
};
-const int translate_fmt[40] =
- {
- -1, FMT_A, FMT_AHEX, FMT_COMMA, FMT_DOLLAR, FMT_F, FMT_IB,
- FMT_PIBHEX, FMT_P, FMT_PIB, FMT_PK, FMT_RB, FMT_RBHEX, -1,
- -1, FMT_Z, FMT_N, FMT_E, -1, -1, FMT_DATE, FMT_TIME,
- FMT_DATETIME, FMT_ADATE, FMT_JDATE, FMT_DTIME, FMT_WKDAY,
- FMT_MONTH, FMT_MOYR, FMT_QYR, FMT_WKYR, FMT_PCT, FMT_DOT,
- FMT_CCA, FMT_CCB, FMT_CCC, FMT_CCD, FMT_CCE, FMT_EDATE,
- FMT_SDATE,
- };
-
+/* Parses the alphabetic prefix of the current token as a format
+ specifier name. Returns the corresponding format specifier
+ type if successful, or -1 on failure. If ALLOW_XT is zero,
+ then X and T format specifiers are not allowed. If CP is
+ nonzero, then *CP is set to the first non-alphabetic character
+ in the current token on success or to a null pointer on
+ failure. */
int
parse_format_specifier_name (const char **cp, int allow_xt)
{
- struct fmt_desc *f;
- char *ep;
- int x;
+ char *sp, *ep;
+ int idx;
- ep = ds_value (&tokstr);
+ sp = ep = ds_value (&tokstr);
while (isalpha ((unsigned char) *ep))
ep++;
- x = *ep;
- *ep = 0;
-
- for (f = formats; f->name[0]; f++)
- if (!strcmp (f->name, ds_value (&tokstr)))
- {
- int indx = f - formats;
- *ep = x;
- if (cp)
- *cp = ep;
+ if (sp != ep)
+ {
+ /* Find format. */
+ for (idx = 0; idx < FMT_NUMBER_OF_FORMATS; idx++)
+ if (strlen (formats[idx].name) == ep - sp
+ && memcmp (formats[idx].name, sp, ep - sp))
+ break;
- if (!allow_xt && (indx == FMT_T || indx == FMT_X))
- {
- msg (SE, _("X and T format specifiers not allowed here."));
- return -1;
- }
- return indx;
- }
+ /* Check format. */
+ if (idx < FMT_NUMBER_OF_FORMATS)
+ {
+ if (!allow_xt && (idx == FMT_T || idx == FMT_X))
+ {
+ msg (SE, _("X and T format specifiers not allowed here."));
+ idx = -1;
+ }
+ }
+ else
+ {
+ /* No match. */
+ msg (SE, _("%.*s is not a valid data format."),
+ (int) (ep - sp), ds_value (&tokstr));
+ idx = -1;
+ }
+ }
+ else
+ {
+ lex_error ("expecting data format");
+ idx = -1;
+ }
+
+ if (cp != NULL)
+ *cp = ep;
- msg (SE, _("%s is not a valid data format."), ds_value (&tokstr));
- *ep = x;
- return -1;
+ return idx;
}
/* Converts F to its string representation (for instance, "F8.2") and
return buf;
}
+/* Checks whether SPEC is valid as an input format and returns
+ nonzero if so. Otherwise, emits an error message and returns
+ zero. */
int
check_input_specifier (const struct fmt_spec *spec)
{
return 1;
}
+/* Checks whether SPEC is valid as an output format and returns
+ nonzero if so. Otherwise, emits an error message and returns
+ zero. */
int
check_output_specifier (const struct fmt_spec *spec)
{
return 1;
}
+/* Converts input format specifier INPUT into output format
+ specifier OUTPUT. */
void
convert_fmt_ItoO (const struct fmt_spec *input, struct fmt_spec *output)
{
}
}
+/* Parses a format specifier from the token stream and returns
+ nonzero only if successful. Emits an error message on
+ failure. Allows X and T format specifiers only if ALLOW_XT is
+ nonzero. The caller should call check_input_specifier() or
+ check_output_specifier() on the parsed format as
+ necessary. */
int
parse_format_specifier (struct fmt_spec *input, int allow_xt)
{
return 1;
}
+/* Returns the width corresponding to the format specifier. The
+ return value is the value of the `width' member of a `struct
+ variable' for such an input format. */
int
get_format_var_width (const struct fmt_spec *spec)
{
else
return 0;
}
+
+/* Returns the PSPP format corresponding to the given SPSS
+ format. */
+int
+translate_fmt (int spss)
+{
+ int type;
+
+ for (type = 0; type < FMT_NUMBER_OF_FORMATS; type++)
+ if (formats[type].spss == spss)
+ return type;
+ return -1;
+}
/* Descriptions of all the display formats above. */
extern struct fmt_desc formats[];
-/* Translates SPSS formats to PSPP formats. */
-extern const int translate_fmt[40];
-
union value;
/* Maximum length of formatted value, in character. */
int get_format_var_width (const struct fmt_spec *);
int parse_string_as_format (const char *s, int len, const struct fmt_spec *fp,
int fc, union value *v);
+int translate_fmt (int spss);
void data_out (char *s, const struct fmt_spec *fp, const union value *v);
char *fmt_to_string (const struct fmt_spec *);
void num_to_string (double v, char *s, int w, int d);
return 0;
}
+/* Returns nonzero iff the value in struct freq F is non-missing
+ for variable V. */
static int
not_missing (const void *f_, void *v_)
{
return !is_missing (&f->v, v);
}
+/* Summarizes the frequency table data for variable V. */
static void
-postprocess_freq_tab (struct variable * v)
+postprocess_freq_tab (struct variable *v)
{
hsh_compare_func *compare;
struct freq_tab *ft;
}
}
+/* Frees the frequency table for variable V. */
static void
cleanup_freq_tab (struct variable *v)
{
#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
{
{
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;
}
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, int case_num UNUSED)
+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)
{
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))
;
}
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 ();
}
while (mtf_head && mtf_head->type == MTF_FILE)
- if (!mtf_processing (temp_case, NULL))
+ if (!mtf_processing (NULL, NULL))
break;
}
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;
+ compaction_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 (compaction_case->data[v->p.mtf.master->fv].s,
+ record[v->fv].s, v->width);
}
}
}
/* 6. Write the output record. */
- process_active_file_output_case ();
+ process_active_file_output_case (compaction_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;
}
return CMD_SUCCESS;
}
+/* Writes case C to the EXPORT file. */
static int
export_write_case_func (struct ccase *c, void *aux)
{
struct dictionary *default_dict;
struct expression *process_if_expr;
-struct ccase *temp_case;
-
struct trns_header **t_trns;
int n_trns;
int m_trns;
{
enum value_init_type *init; /* How to initialize each `union value'. */
size_t init_cnt; /* Number of elements in inp_init. */
+ size_t case_size; /* Size of case in bytes. */
};
static trns_proc_func end_case_trns_proc, reread_trns_proc, end_file_trns_proc;
lex_match_id ("PROGRAM");
discard_variables ();
- vfm_source = create_case_source (&input_program_source_class, NULL);
+ /* FIXME: we shouldn't do this here, but I'm afraid that other
+ code will check the class of vfm_source. */
+ vfm_source = create_case_source (&input_program_source_class,
+ default_dict, NULL);
return lex_end_of_command ();
}
ordinary transformations. */
f_trns = n_trns;
- /* Figure out how to initialize temp_case. */
+ /* Figure out how to initialize each input case. */
inp = xmalloc (sizeof *inp);
inp->init_cnt = dict_get_next_value_idx (default_dict);
inp->init = xmalloc (inp->init_cnt * sizeof *inp->init);
}
for (i = 0; i < inp->init_cnt; i++)
assert (inp->init[i] != -1);
+ inp->case_size = dict_get_case_size (default_dict);
/* Put inp into vfm_source for later use. */
vfm_source->aux = inp;
+ /* FIXME: we should use create_case_source() here. */
+ vfm_source->value_cnt = dict_get_next_value_idx (default_dict);
+
return lex_end_of_command ();
}
-/* Initializes temp_case. Called before the first case is read. */
+/* Initializes case C. Called before the first case is read. */
static void
-init_case (struct input_program_pgm *inp)
+init_case (const struct input_program_pgm *inp, struct ccase *c)
{
size_t i;
switch (inp->init[i])
{
case INP_NUMERIC | INP_INIT_ONCE:
- temp_case->data[i].f = 0.0;
+ c->data[i].f = 0.0;
break;
case INP_NUMERIC | INP_REINIT:
- temp_case->data[i].f = SYSMIS;
+ c->data[i].f = SYSMIS;
break;
case INP_STRING | INP_INIT_ONCE:
case INP_STRING | INP_REINIT:
- memset (temp_case->data[i].s, ' ', sizeof temp_case->data[i].s);
+ memset (c->data[i].s, ' ', sizeof c->data[i].s);
break;
default:
assert (0);
}
}
-/* Clears temp_case. Called between reading successive records. */
+/* Clears case C. Called between reading successive records. */
static void
-clear_case (struct input_program_pgm *inp)
+clear_case (const struct input_program_pgm *inp, struct ccase *c)
{
size_t i;
case INP_NUMERIC | INP_INIT_ONCE:
break;
case INP_NUMERIC | INP_REINIT:
- temp_case->data[i].f = SYSMIS;
+ c->data[i].f = SYSMIS;
break;
case INP_STRING | INP_INIT_ONCE:
break;
case INP_STRING | INP_REINIT:
- memset (temp_case->data[i].s, ' ', sizeof temp_case->data[i].s);
+ memset (c->data[i].s, ' ', sizeof c->data[i].s);
break;
default:
assert (0);
return value is the index of the transformation to go to next. */
static void
input_program_source_read (struct case_source *source,
+ struct ccase *c,
write_case_func *write_case,
write_case_data wc_data)
{
int cases_written = 0;
assert (inp != NULL);
-
+
/* Figure end_case. */
for (i = 0; i < f_trns; i++)
if (t_trns[i]->proc == end_case_trns_proc)
end_case = 1;
- /* FIXME: This code should not be necessary. It is an ugly
- kluge. */
+ /* FIXME: This is an ugly kluge. */
for (i = 0; i < f_trns; i++)
if (t_trns[i]->proc == repeating_data_trns_proc)
repeating_data_set_write_case (t_trns[i], write_case, wc_data);
- init_case (inp);
+ init_case (inp, c);
for (;;)
{
- /* Index of current transformation. */
- int i;
-
- /* Return value of last-called transformation. */
- int code;
-
- debug_printf (("input-program: "));
-
/* Perform transformations on `blank' case. */
- for (i = 0; i < f_trns;)
+ for (i = 0; i < f_trns; )
{
-#if DEBUGGING
- printf ("/%d", i);
- if (t_trns[i]->proc == end_case_trns_proc)
- printf ("\n");
-#endif
+ int code; /* Return value of last-called transformation. */
if (t_trns[i]->proc == end_case_trns_proc)
{
cases_written++;
if (!write_case (wc_data))
- return;
- clear_case (inp);
+ goto done;
+ clear_case (inp, c);
i++;
continue;
}
- code = t_trns[i]->proc (t_trns[i], temp_case, cases_written + 1);
+ code = t_trns[i]->proc (t_trns[i], c, cases_written + 1);
switch (code)
{
case -1:
i++;
break;
case -2:
- return;
+ goto done;
case -3:
goto next_case;
default:
}
}
-#if DEBUGGING
- if (!end_case)
- printf ("\n");
-#endif
-
/* Write the case if appropriate. */
- if (!end_case)
- if (!write_case (wc_data))
- return;
+ if (!end_case)
+ {
+ cases_written++;
+ if (!write_case (wc_data))
+ break;
+ }
/* Blank out the case for the next iteration. */
next_case:
- clear_case (inp);
+ clear_case (inp, c);
}
+ done: ;
}
+/* Destroys an INPUT PROGRAM source. */
static void
input_program_source_destroy (struct case_source *source)
{
return lex_end_of_command ();
}
+/* Should never be called, because this is handled in
+ input_program_source_read(). */
int
end_case_trns_proc (struct trns_header *t UNUSED, struct ccase * c UNUSED,
int case_num UNUSED)
return CMD_SUCCESS;
}
+/* Executes a REREAD transformation. */
static int
reread_trns_proc (struct trns_header * pt, struct ccase * c,
int case_num)
return -1;
}
+/* Frees a REREAD transformation. */
static void
reread_trns_free (struct trns_header * t)
{
return lex_end_of_command ();
}
+/* Executes an END FILE transformation. */
static int
end_file_trns_proc (struct trns_header * t UNUSED, struct ccase * c UNUSED,
int case_num UNUSED)
{
-#if DEBUGGING
- printf ("END FILE\n");
-#endif
return -2;
}
#include "debug-print.h"
-/* *INDENT-OFF* */
/* LOOP strategy:
Each loop causes 3 different transformations to be output. The
transformation to -1. This ensures that the pass number is set to
-1 every time the loop is encountered, before the first iteration.
- The second transformation increments the pass number. If there is
- no indexing or test clause on either LOOP or END LOOP, then the
- pass number is checked against MXLOOPS and control may pass out of
- the loop; otherwise the indexing or test clause(s) on LOOP are
- checked, and again control may pass out of the loop.
+ The second transformation increments the pass number. If
+ there is no indexing or test clause on either LOOP or END
+ LOOP, then the pass number is checked against MXLOOPS and
+ control may pass out of the loop. Otherwise the indexing or
+ test clause(s) on LOOP are checked, and again control may pass
+ out of the loop.
- After the second transformation the body of the loop is executed.
+ After the second transformation the body of the loop is
+ executed.
The last transformation checks the test clause if present and
- either jumps back up to the second transformation or terminates the
- loop.
-
- Flow of control: (The characters ^V<> represents arrows.)
-
- 1. LOOP (sets pass # to -1)
- V
- V
- >>2. LOOP (increment pass number)
- ^ (test optional indexing clause)
- ^ (test optional IF clause)
- ^ if we need another trip if we're done with the loop>>V
- ^ V V
- ^ V V
- ^ *. execute loop body V
- ^ . V
- ^ . (any number of transformations) V
- ^ . V
- ^ V
- ^ 3. END LOOP (test optional IF clause) V
- ^<<<<if we need another trip if we're done with the loop>>V
- V
- V
- *. transformations after loop body<<<<<<<<<<<<<<<<<<<<<<<<<<<
+ either jumps back up to the second transformation or
+ terminates the loop.
+ Flow of control:
+
+ 1. LOOP. Sets pass number to -1 and continues to next
+ transformation.
+
+ 2. LOOP. Increments pass number. Tests optional indexing
+ clause and optional IF clause. If we're done with the
+ loop, we jump to the transformation just after LOOP
+ transformation 3.
+
+ Otherwise, we continue through the transformations in the
+ loop body.
+
+ 3. END LOOP. We test the optional IF clause. If we need to
+ make another pass through the loop, we jump to LOOP
+ transformation 2.
+
+ Otherwise, we continue with the transformation jump after
+ the loop.
*/
-/* *INDENT-ON* */
/* Types of limits on loop execution. */
enum
int contents[EOC * 3 + 1]; /* Contents. */
int n_contents; /* Number of entries. */
-
+ /* Continuous variables. */
int n_continuous; /* Number of continuous variables. */
int first_continuous; /* Index into default_dict.var of
first continuous variable. */
MSTR /* String. */
};
+/* A MATRIX DATA parsing token. */
struct matrix_token
{
enum matrix_token_type type;
static int nr_read_splits (struct matrix_data_pgm *, int compare);
static int nr_read_factors (struct matrix_data_pgm *, int cell);
-static void nr_output_data (struct matrix_data_pgm *,
+static void nr_output_data (struct matrix_data_pgm *, struct ccase *,
write_case_func *, write_case_data);
static void matrix_data_read_without_rowtype (struct case_source *source,
+ struct ccase *,
write_case_func *,
write_case_data);
max_cell_index = 0;
vfm_source = create_case_source (&matrix_data_without_rowtype_source_class,
- mx);
+ default_dict, mx);
procedure (NULL, NULL, NULL, NULL);
writes them to the output file. Returns success. */
static void
matrix_data_read_without_rowtype (struct case_source *source,
+ struct ccase *c,
write_case_func *write_case,
write_case_data wc_data)
{
}
}
- nr_output_data (mx, write_case, wc_data);
+ nr_output_data (mx, c, write_case, wc_data);
if (dict_get_split_cnt (default_dict) == 0
|| !another_token (mx->data_file))
CP to the active file. */
static void
dump_cell_content (struct matrix_data_pgm *mx, int content, double *cp,
+ struct ccase *c,
write_case_func *write_case, write_case_data wc_data)
{
int type = content_type[content];
{
- st_bare_pad_copy (temp_case->data[mx->rowtype_->fv].s,
+ st_bare_pad_copy (c->data[mx->rowtype_->fv].s,
content_names[content], 8);
if (type != 1)
- memset (&temp_case->data[mx->varname_->fv].s, ' ', 8);
+ memset (&c->data[mx->varname_->fv].s, ' ', 8);
}
{
for (j = 0; j < mx->n_continuous; j++)
{
int fv = dict_get_var (default_dict, mx->first_continuous + j)->fv;
- temp_case->data[fv].f = *cp;
+ c->data[fv].f = *cp;
cp++;
}
if (type == 1)
- st_bare_pad_copy (temp_case->data[mx->varname_->fv].s,
+ st_bare_pad_copy (c->data[mx->varname_->fv].s,
dict_get_var (default_dict,
mx->first_continuous + i)->name,
8);
/* Finally dump out everything from nr_data[] to the output file. */
static void
-nr_output_data (struct matrix_data_pgm *mx,
+nr_output_data (struct matrix_data_pgm *mx, struct ccase *c,
write_case_func *write_case, write_case_data wc_data)
{
{
split_cnt = dict_get_split_cnt (default_dict);
split = dict_get_split_vars (default_dict);
for (i = 0; i < split_cnt; i++)
- temp_case->data[split[i]->fv].f = split_values[i];
+ c->data[split[i]->fv].f = split_values[i];
}
if (mx->n_factors)
for (factor = 0; factor < mx->n_factors; factor++)
{
- temp_case->data[mx->factors[factor]->fv].f
+ c->data[mx->factors[factor]->fv].f
= nr_factor_values[factor + cell * mx->n_factors];
debug_printf (("f:%s ", mx->factors[factor]->name));
}
&& nr_data[content][cell] != NULL);
dump_cell_content (mx, content, nr_data[content][cell],
- write_case, wc_data);
+ c, write_case, wc_data);
}
}
}
int factor;
for (factor = 0; factor < mx->n_factors; factor++)
- temp_case->data[mx->factors[factor]->fv].f = SYSMIS;
+ c->data[mx->factors[factor]->fv].f = SYSMIS;
}
for (content = 0; content <= PROX; content++)
if (!mx->is_per_factor[content] && nr_data[content] != NULL)
dump_cell_content (mx, content, nr_data[content][0],
- write_case, wc_data);
+ c, write_case, wc_data);
}
}
\f
struct factor_data *wr_current;
static int wr_read_splits (struct matrix_data_pgm *,
+ struct ccase *,
write_case_func *, write_case_data);
-static int wr_output_data (struct matrix_data_pgm *, write_case_func *, write_case_data);
+static int wr_output_data (struct matrix_data_pgm *,
+ struct ccase *, write_case_func *, write_case_data);
static int wr_read_rowtype (const struct matrix_token *, struct file_handle *);
static int wr_read_factors (struct matrix_data_pgm *);
static int wr_read_indeps (struct matrix_data_pgm *);
static void matrix_data_read_with_rowtype (struct case_source *,
+ struct ccase *,
write_case_func *,
write_case_data);
split_values = NULL;
mx->cells = 0;
- vfm_source = create_case_source (&matrix_data_with_rowtype_source_class, mx);
+ vfm_source = create_case_source (&matrix_data_with_rowtype_source_class,
+ default_dict, mx);
procedure (NULL, NULL, NULL, NULL);
/* Read from the data file and write it to the active file. */
static void
matrix_data_read_with_rowtype (struct case_source *source,
+ struct ccase *c,
write_case_func *write_case,
write_case_data wc_data)
{
do
{
- if (!wr_read_splits (mx, write_case, wc_data))
+ if (!wr_read_splits (mx, c, write_case, wc_data))
return;
if (!wr_read_factors (mx))
}
while (another_token (mx->data_file));
- wr_output_data (mx, write_case, wc_data);
+ wr_output_data (mx, c, write_case, wc_data);
}
/* Read the split file variables. If they differ from the previous
set of split variables then output the data. Returns success. */
static int
wr_read_splits (struct matrix_data_pgm *mx,
+ struct ccase *c,
write_case_func *write_case, write_case_data wc_data)
{
int compare;
if (compare && split_values[i] != token.number && !different)
{
- if (!wr_output_data (mx, write_case, wc_data))
+ if (!wr_output_data (mx, c, write_case, wc_data))
return 0;
different = 1;
mx->cells = 0;
file. */
static int
wr_output_data (struct matrix_data_pgm *mx,
+ struct ccase *c,
write_case_func *write_case, write_case_data wc_data)
{
{
split_cnt = dict_get_split_cnt (default_dict);
split = dict_get_split_vars (default_dict);
for (i = 0; i < split_cnt; i++)
- temp_case->data[split[i]->fv].f = split_values[i];
+ c->data[split[i]->fv].f = split_values[i];
}
/* Sort the wr_data list. */
for (factor = 0; factor < mx->n_factors; factor++)
{
- temp_case->data[mx->factors[factor]->fv].f
+ c->data[mx->factors[factor]->fv].f
= iter->factors[factor];
debug_printf (("f:%s ", factors[factor]->name));
}
fill_matrix (mx, content, iter->data[content]);
dump_cell_content (mx, content, iter->data[content],
- write_case, wc_data);
+ c, write_case, wc_data);
}
}
}
convert_format (struct file_handle *h, int fmt[3], struct fmt_spec *v,
struct variable *vv)
{
- if (fmt[0] < 0
- || (size_t) fmt[0] >= sizeof translate_fmt / sizeof *translate_fmt)
+ v->type = translate_fmt (fmt[0]);
+ if (v->type == -1)
lose ((h, _("%s: Bad format specifier byte (%d)."), vv->name, fmt[0]));
-
- v->type = translate_fmt[fmt[0]];
v->w = fmt[1];
v->d = fmt[2];
char_name = local_char_name;
if (ext->current->font->ligatures && *cp == 'f')
{
- int lig;
+ int lig = 0;
char_name = NULL;
if (cp < end - 1)
}
if ((lig & ext->current->font->ligatures) == 0)
{
- local_char_name[0] = *cp++; /* 'f' */
+ local_char_name[0] = *cp; /* 'f' */
char_name = local_char_name;
}
}
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA. */
+/* FIXME: seems like a lot of code duplication with data-list.c. */
+
#include <config.h>
#include <assert.h>
#include <stdlib.h>
\f
/* Definitions. */
+/* Type of source value for RECODE. */
enum
{
RCD_END, /* sentinel value */
return lex_end_of_command ();
}
+/* Executes a SAMPLE transformation. */
static int
sample_trns_proc (struct trns_header * trns, struct ccase *c UNUSED,
int case_num UNUSED)
static int
parse_format_spec (struct file_handle *h, int32 s, struct fmt_spec *v, struct variable *vv)
{
- if ((size_t) ((s >> 16) & 0xff)
- >= sizeof translate_fmt / sizeof *translate_fmt)
+ v->type = translate_fmt ((s >> 16) & 0xff);
+ if (v->type == -1)
lose ((ME, _("%s: Bad format specifier byte (%d)."),
h->fn, (s >> 16) & 0xff));
-
- v->type = translate_fmt[(s >> 16) & 0xff];
v->w = (s >> 8) & 0xff;
v->d = s & 0xff;
return NULL;
}
+/* Destroys a SORT CASES program. */
void
destroy_sort_cases_pgm (struct sort_cases_pgm *scp)
{
int
sort_cases (struct sort_cases_pgm *scp, int separate)
{
+ scp->case_size = sizeof (union value) * compaction_nval;
+
/* Not sure this is necessary but it's good to be safe. */
if (separate && case_source_is_class (vfm_source, &sort_source_class))
procedure (NULL, NULL, NULL, NULL);
return 0;
}
\f
+/* Results of an internal sort. */
struct internal_sort
{
struct case_list **results;
return NULL;
}
+/* Destroys an internal sort result. */
static void
destroy_internal_sort (struct internal_sort *isrt)
{
return a->case_cnt > b->case_cnt ? -1 : a->case_cnt <b->case_cnt;
}
+/* Results of an external sort. */
struct external_sort
{
struct sort_cases_pgm *scp; /* SORT CASES info. */
struct initial_run *initial_runs; /* Array of initial runs. */
size_t run_cnt, run_cap; /* Number of runs, allocated capacity. */
char *temp_dir; /* Temporary file directory name. */
+ char *temp_name; /* Name of a temporary file. */
int next_file_idx; /* Lowest unused file index. */
- size_t case_size; /* Number of bytes in case. */
};
/* Prototypes for helper functions. */
-static void sort_sink_write (struct case_sink *, struct ccase *);
+static void sort_sink_write (struct case_sink *, const struct ccase *);
static int write_initial_runs (struct external_sort *, int separate);
static int init_external_sort (struct external_sort *);
static int merge (struct external_sort *);
xsrt = xmalloc (sizeof *xsrt);
xsrt->scp = scp;
- xsrt->case_size = sizeof (union value) * compaction_nval;
if (!init_external_sort (xsrt))
goto done;
if (!write_initial_runs (xsrt, separate))
for (i = 0; i < xsrt->run_cnt; i++)
remove_temp_file (xsrt, xsrt->initial_runs[i].file_idx);
rmdir_temp_dir (xsrt);
+ free (xsrt->temp_dir);
+ free (xsrt->temp_name);
free (xsrt->initial_runs);
free (xsrt);
}
/* Temporary directory. */
xsrt->temp_dir = make_temp_dir ();
+ xsrt->temp_name = NULL;
if (xsrt->temp_dir == NULL)
return 0;
+ xsrt->temp_name = xmalloc (strlen (xsrt->temp_dir) + 64);
return 1;
}
-
+/* Returns nonzero if we should return an error even though the
+ operation succeeded. Useful for testing. */
static int
simulate_error (void)
{
}
}
-#define TEMP_FILE_NAME_SIZE (L_tmpnam + 32)
-static void
-get_temp_file_name (struct external_sort *xsrt, int file_idx,
- char filename[TEMP_FILE_NAME_SIZE])
+/* Returns the name of temporary file number FILE_IDX for XSRT.
+ The name is written into a static buffer, so be careful. */
+static char *
+get_temp_file_name (struct external_sort *xsrt, int file_idx)
{
assert (xsrt->temp_dir != NULL);
- sprintf (filename, "%s%c%04d", xsrt->temp_dir, DIR_SEPARATOR, file_idx);
+ sprintf (xsrt->temp_name, "%s%c%04d",
+ xsrt->temp_dir, DIR_SEPARATOR, file_idx);
+ return xsrt->temp_name;
}
+/* Opens temporary file numbered FILE_IDX for XSRT with mode MODE
+ and returns the FILE *. */
static FILE *
open_temp_file (struct external_sort *xsrt, int file_idx, const char *mode)
{
- char temp_file[TEMP_FILE_NAME_SIZE];
+ char *temp_file;
FILE *file;
- get_temp_file_name (xsrt, file_idx, temp_file);
+ temp_file = get_temp_file_name (xsrt, file_idx);
file = fopen (temp_file, mode);
if (simulate_error () || file == NULL)
return file;
}
+/* Closes FILE, which is the temporary file numbered FILE_IDX
+ under XSRT. Returns nonzero only if successful. */
static int
close_temp_file (struct external_sort *xsrt, int file_idx, FILE *file)
{
if (file != NULL)
{
- char temp_file[TEMP_FILE_NAME_SIZE];
- get_temp_file_name (xsrt, file_idx, temp_file);
+ char *temp_file = get_temp_file_name (xsrt, file_idx);
if (simulate_error () || fclose (file) == EOF)
{
msg (SE, _("%s: Error closing temporary file: %s."),
return 1;
}
+/* Delete temporary file numbered FILE_IDX for XSRT. */
static void
remove_temp_file (struct external_sort *xsrt, int file_idx)
{
if (file_idx != -1)
{
- char temp_file[TEMP_FILE_NAME_SIZE];
- get_temp_file_name (xsrt, file_idx, temp_file);
+ char *temp_file = get_temp_file_name (xsrt, file_idx);
if (simulate_error () || remove (temp_file) != 0)
msg (SE, _("%s: Error removing temporary file: %s."),
temp_file, strerror (errno));
}
}
+/* Writes SIZE bytes from buffer DATA into FILE, which is
+ temporary file numbered FILE_IDX from XSRT. */
static int
write_temp_file (struct external_sort *xsrt, int file_idx,
FILE *file, const void *data, size_t size)
return 1;
else
{
- char temp_file[TEMP_FILE_NAME_SIZE];
- get_temp_file_name (xsrt, file_idx, temp_file);
+ char *temp_file = get_temp_file_name (xsrt, file_idx);
msg (SE, _("%s: Error writing temporary file: %s."),
temp_file, strerror (errno));
return 0;
}
}
+/* Reads SIZE bytes into buffer DATA into FILE, which is
+ temporary file numbered FILE_IDX from XSRT. */
static int
read_temp_file (struct external_sort *xsrt, int file_idx,
FILE *file, void *data, size_t size)
return 1;
else
{
- char temp_file[TEMP_FILE_NAME_SIZE];
- get_temp_file_name (xsrt, file_idx, temp_file);
+ char *temp_file = get_temp_file_name (xsrt, file_idx);
if (ferror (file))
msg (SE, _("%s: Error reading temporary file: %s."),
temp_file, strerror (errno));
struct case_list *record; /* Case data. */
};
+/* Represents a set of initial runs during an external sort. */
struct initial_run_state
{
struct external_sort *xsrt;
struct sort_cases_pgm *);
static int compare_record_run_minheap (const void *, const void *, void *);
+/* Writes initial runs for XSRT, sending them to a separate file
+ if SEPARATE is nonzero. */
static int
write_initial_runs (struct external_sort *xsrt, int separate)
{
/* Create case sink. */
if (!separate)
{
- if (vfm_sink)
+ if (vfm_sink != NULL && vfm_sink->class->destroy != NULL)
vfm_sink->class->destroy (vfm_sink);
vfm_sink = create_case_sink (&sort_sink_class, irs);
xsrt->scp->ref_cnt++;
/* Add a single case to an initial run. */
static void
-sort_sink_write (struct case_sink *sink, struct ccase *c)
+sort_sink_write (struct case_sink *sink, const struct ccase *c)
{
struct initial_run_state *irs = sink->aux;
struct record_run *new_record_run;
output_record (irs);
}
+/* Destroys the initial run state represented by IRS. */
static void
destroy_initial_run_state (struct initial_run_state *irs)
{
return 0;
}
+/* Compares record-run tuples A and B on run number first, then
+ on the current record according to SCP. */
static int
compare_record_run (const struct record_run *a,
const struct record_run *b,
return compare_record (a->record->c.data, b->record->c.data, scp);
}
+/* Compares record-run tuples A and B on run number first, then
+ on the current record according to SCP, but in descending
+ order. */
static int
compare_record_run_minheap (const void *a, const void *b, void *scp)
{
irs->output_file = NULL;
}
+/* Writes a record to the current initial run. */
static void
output_record (struct initial_run_state *irs)
{
irs->last_output = record_run->record;
}
+/* Gets a case from the free list in IRS. It is an error to call
+ this function if the free list is empty. */
static struct case_list *
grab_case (struct initial_run_state *irs)
{
return c;
}
+/* Returns C to the free list in IRS. */
static void
release_case (struct initial_run_state *irs, struct case_list *c)
{
\f
/* Merging. */
+/* State of merging initial runs. */
struct merge_state
{
struct external_sort *xsrt; /* External sort state. */
return 1;
}
\f
-static void
-sort_sink_destroy (struct case_sink *sink UNUSED)
-{
- assert (0);
-}
-
static struct case_source *
sort_sink_make_source (struct case_sink *sink)
{
struct initial_run_state *irs = sink->aux;
- return create_case_source (&sort_source_class, irs->xsrt->scp);
+ return create_case_source (&sort_source_class, default_dict,
+ irs->xsrt->scp);
}
const struct case_sink_class sort_sink_class =
"SORT CASES",
NULL,
sort_sink_write,
- sort_sink_destroy,
+ NULL,
sort_sink_make_source,
};
\f
+struct sort_source_aux
+ {
+ struct sort_cases_pgm *scp;
+ struct ccase *dst;
+ write_case_func *write_case;
+ write_case_data wc_data;
+ };
+
+/* Passes C to the write_case function. */
+static int
+sort_source_read_helper (const struct ccase *src, void *aux_)
+{
+ struct sort_source_aux *aux = aux_;
+
+ memcpy (aux->dst, src, aux->scp->case_size);
+ return aux->write_case (aux->wc_data);
+}
+
/* Reads all the records from the source stream and passes them
to write_case(). */
static void
sort_source_read (struct case_source *source,
+ struct ccase *c,
write_case_func *write_case, write_case_data wc_data)
{
struct sort_cases_pgm *scp = source->aux;
+ struct sort_source_aux aux;
+
+ aux.scp = scp;
+ aux.dst = c;
+ aux.write_case = write_case;
+ aux.wc_data = wc_data;
- read_sort_output (scp, write_case, wc_data);
+ read_sort_output (scp, sort_source_read_helper, &aux);
}
-void read_internal_sort_output (struct internal_sort *isrt,
- write_case_func *write_case,
- write_case_data wc_data);
-void read_external_sort_output (struct external_sort *xsrt,
- write_case_func *write_case,
- write_case_data wc_data);
+static void read_internal_sort_output (struct internal_sort *isrt,
+ read_sort_output_func *, void *aux);
+static void read_external_sort_output (struct external_sort *xsrt,
+ read_sort_output_func *, void *aux);
/* Reads all the records from the output stream and passes them to the
function provided, which must have an interface identical to
write_case(). */
void
read_sort_output (struct sort_cases_pgm *scp,
- write_case_func *write_case, write_case_data wc_data)
+ read_sort_output_func *output_func, void *aux)
{
assert ((scp->isrt != NULL) + (scp->xsrt != NULL) <= 1);
if (scp->isrt != NULL)
- read_internal_sort_output (scp->isrt, write_case, wc_data);
+ read_internal_sort_output (scp->isrt, output_func, aux);
else if (scp->xsrt != NULL)
- read_external_sort_output (scp->xsrt, write_case, wc_data);
+ read_external_sort_output (scp->xsrt, output_func, aux);
else
{
/* No results. Probably an external sort that failed. */
}
}
-void
+static void
read_internal_sort_output (struct internal_sort *isrt,
- write_case_func *write_case,
- write_case_data wc_data)
+ read_sort_output_func *output_func,
+ void *aux)
{
- struct ccase *save_temp_case = temp_case;
struct case_list **p;
- for (p = isrt->results; *p; p++)
- {
- temp_case = &(*p)->c;
- write_case (wc_data);
- }
+ for (p = isrt->results; *p; p++)
+ if (!output_func (&(*p)->c, aux))
+ break;
free (isrt->results);
-
- temp_case = save_temp_case;
}
-void
+static void
read_external_sort_output (struct external_sort *xsrt,
- write_case_func *write_case,
- write_case_data wc_data)
+ read_sort_output_func *output_func, void *aux)
{
FILE *file;
int file_idx;
size_t i;
+ struct ccase *c;
assert (xsrt->run_cnt == 1);
file_idx = xsrt->initial_runs[0].file_idx;
return;
}
+ c = xmalloc (xsrt->scp->case_size);
for (i = 0; i < xsrt->initial_runs[0].case_cnt; i++)
{
- if (!read_temp_file (xsrt, file_idx, file,
- temp_case, xsrt->case_size))
+ if (!read_temp_file (xsrt, file_idx, file, c, xsrt->scp->case_size))
{
err_failure ();
break;
}
- if (!write_case (wc_data))
+ if (!output_func (c, aux))
break;
}
+ free (c);
}
static void
struct internal_sort *isrt; /* Internal sort output. */
struct external_sort *xsrt; /* External sort output. */
+ size_t case_size; /* Number of bytes in case. */
};
/* SORT CASES programmatic interface. */
+
+typedef int read_sort_output_func (const struct ccase *, void *aux);
+
struct sort_cases_pgm *parse_sort (void);
int sort_cases (struct sort_cases_pgm *, int separate);
void read_sort_output (struct sort_cases_pgm *,
- write_case_func *, write_case_data);
+ read_sort_output_func, void *aux);
void destroy_sort_cases_pgm (struct sort_cases_pgm *);
#endif /* !sort_h */
#include "misc.h"
#include "str.h"
+/* Parses a name as a variable within VS and returns the variable
+ if successful. On failure emits an error message and returns
+ a null pointer. */
static struct variable *
parse_vs_variable (struct var_set *vs)
{
return vp;
}
+/* Parses a variable name in dictionary D and returns the
+ variable if successful. On failure emits an error message and
+ returns a null pointer. */
struct variable *
parse_dict_variable (struct dictionary *d)
{
return var;
}
+/* Parses a variable name in default_dict and returns the
+ variable if successful. On failure emits an error message and
+ returns a null pointer. */
struct variable *
parse_variable (void)
{
return parse_dict_variable (default_dict);
}
-
+/* Returns the dictionary class corresponding to a variable named
+ NAME. */
enum dict_class
dict_class_from_id (const char *name)
{
}
}
+/* Returns the name of dictionary class DICT_CLASS. */
const char *
dict_class_to_name (enum dict_class dict_class)
{
switch (dict_class)
{
case DC_ORDINARY:
- return "ordinary";
+ return _("ordinary");
case DC_SYSTEM:
- return "system";
+ return _("system");
case DC_SCRATCH:
- return "scratch";
+ return _("scratch");
default:
assert (0);
}
}
+/* Parses a set of variables from dictionary D given options
+ OPTS. Resulting list of variables stored in *VAR and the
+ number of variables into *CNT. */
int
parse_variables (struct dictionary *d, struct variable ***var, int *cnt,
int opts)
return 0;
}
+/* Extracts a numeric suffix from variable name S, copying it
+ into string R. Sets *D to the length of R and *N to its
+ value. */
static int
extract_num (char *s, char *r, int *n, int *d)
{
return 0;
}
\f
+/* A set of variables. */
struct var_set
{
size_t (*get_cnt) (struct var_set *);
void *aux;
};
+/* Returns the number of variables in VS. */
size_t
var_set_get_cnt (struct var_set *vs)
{
return vs->get_cnt (vs);
}
+/* Return variable with index IDX in VS.
+ IDX must be less than the number of variables in VS. */
struct variable *
var_set_get_var (struct var_set *vs, size_t idx)
{
return vs->get_var (vs, idx);
}
+/* Returns the variable in VS named NAME, or a null pointer if VS
+ contains no variable with that name. */
struct variable *
var_set_lookup_var (struct var_set *vs, const char *name)
{
return vs->lookup_var (vs, name);
}
+/* Destroys VS. */
void
var_set_destroy (struct var_set *vs)
{
vs->destroy (vs);
}
\f
+/* Returns the number of variables in VS. */
static size_t
dict_var_set_get_cnt (struct var_set *vs)
{
return dict_get_var_cnt (d);
}
+/* Return variable with index IDX in VS.
+ IDX must be less than the number of variables in VS. */
static struct variable *
dict_var_set_get_var (struct var_set *vs, size_t idx)
{
return dict_get_var (d, idx);
}
+/* Returns the variable in VS named NAME, or a null pointer if VS
+ contains no variable with that name. */
static struct variable *
dict_var_set_lookup_var (struct var_set *vs, const char *name)
{
return dict_lookup_var (d, name);
}
+/* Destroys VS. */
static void
dict_var_set_destroy (struct var_set *vs)
{
free (vs);
}
+/* Returns a variable set based on D. */
struct var_set *
var_set_create_from_dict (struct dictionary *d)
{
return vs;
}
\f
+/* A variable set based on an array. */
struct array_var_set
{
- struct variable **var;
- size_t var_cnt;
- struct hsh_table *name_tab;
+ struct variable **var; /* Array of variables. */
+ size_t var_cnt; /* Number of elements in var. */
+ struct hsh_table *name_tab; /* Hash from variable names to variables. */
};
+/* Returns the number of variables in VS. */
static size_t
array_var_set_get_cnt (struct var_set *vs)
{
return avs->var_cnt;
}
+/* Return variable with index IDX in VS.
+ IDX must be less than the number of variables in VS. */
static struct variable *
array_var_set_get_var (struct var_set *vs, size_t idx)
{
return avs->var[idx];
}
+/* Returns the variable in VS named NAME, or a null pointer if VS
+ contains no variable with that name. */
static struct variable *
array_var_set_lookup_var (struct var_set *vs, const char *name)
{
return hsh_find (avs->name_tab, &v);
}
+/* Destroys VS. */
static void
array_var_set_destroy (struct var_set *vs)
{
free (vs);
}
+/* Returns a variable set based on the VAR_CNT variables in
+ VAR. */
struct var_set *
var_set_create_from_array (struct variable **var, size_t var_cnt)
{
static int lag_head; /* Index where next case will be added. */
static struct ccase **lag_queue; /* Array of n_lag ccase * elements. */
+static struct ccase *create_trns_case (struct dictionary *dict);
static void open_active_file (void);
static void close_active_file (struct write_case_data *);
static int SPLIT_FILE_proc_func (struct ccase *, void *);
static void finish_compaction (void);
-static void lag_case (void);
-static int procedure_write_case (struct write_case_data *);
-static void clear_temp_case (void);
-static int exclude_this_case (int case_num);
+static void lag_case (const struct ccase *);
+static write_case_func procedure_write_case;
+static void clear_case (struct ccase *);
+static int exclude_this_case (const struct ccase *, int case_num);
\f
/* Public functions. */
+/* Auxiliary data for executing a procedure. */
struct procedure_aux_data
{
+ struct ccase *trns_case; /* Case used for transformations. */
size_t cases_written; /* Number of cases written so far. */
};
+/* Auxiliary data for SPLIT FILE. */
struct split_aux_data
{
struct ccase *prev_case; /* Data in previous case. */
};
/* Reads all the cases from the active file, transforms them by
- the active set of transformations, calls PROC_FUNC with CURCASE
- set to the case, and writes them to a new active file.
+ the active set of transformations, passes each of them to
+ PROC_FUNC, and writes them to a new active file.
Divides the active file into zero or more series of one or more
cases each. BEGIN_FUNC is called before each series. END_FUNC is
PROC_FUNC, and END_FUNC as auxiliary data. */
void
procedure (void (*begin_func) (void *),
- int (*proc_func) (struct ccase *curcase, void *),
+ int (*proc_func) (struct ccase *, void *),
void (*end_func) (void *),
void *func_aux)
{
assert (++recursive_call == 1);
proc_aux.cases_written = 0;
+ proc_aux.trns_case = create_trns_case (default_dict);
/* Normally we just use the data passed by the user. */
procedure_write_data.begin_func = begin_func;
open_active_file ();
if (vfm_source != NULL)
vfm_source->class->read (vfm_source,
+ proc_aux.trns_case,
procedure_write_case, &procedure_write_data);
close_active_file (&procedure_write_data);
if (split)
free (split_aux.prev_case);
+ free (proc_aux.trns_case);
+
assert (--recursive_call == 0);
}
\f
/* Active file processing support. Subtly different semantics from
procedure(). */
-static int process_active_file_write_case (struct write_case_data *data);
+static write_case_func process_active_file_write_case;
-/* The casefunc might want us to stop calling it. */
+/* The case_func might want us to stop calling it. */
static int not_canceled;
-/* Reads all the cases from the active file and passes them one-by-one
- to CASEFUNC in temp_case. Before any cases are passed, calls
- BEGIN_FUNC. After all the cases have been passed, calls END_FUNC.
- BEGIN_FUNC, CASEFUNC, and END_FUNC can write temp_case to the output
- file by calling process_active_file_output_case().
+/* Reads all the cases from the active file and passes them
+ one-by-one to CASE_FUNC. Before any cases are passed, calls
+ BEGIN_FUNC. After all the cases have been passed, calls
+ END_FUNC. BEGIN_FUNC, CASE_FUNC, and END_FUNC can write to
+ the output file by calling process_active_file_output_case().
process_active_file() ignores TEMPORARY, SPLIT FILE, and N. */
void
process_active_file (void (*begin_func) (void *),
- int (*casefunc) (struct ccase *curcase, void *),
+ int (*case_func) (struct ccase *, void *),
void (*end_func) (void *),
void *func_aux)
{
+ struct procedure_aux_data proc_aux;
struct write_case_data process_active_write_data;
+ proc_aux.cases_written = 0;
+ proc_aux.trns_case = create_trns_case (default_dict);
+
process_active_write_data.begin_func = begin_func;
- process_active_write_data.proc_func = casefunc;
+ process_active_write_data.proc_func = case_func;
process_active_write_data.end_func = end_func;
process_active_write_data.func_aux = func_aux;
+ process_active_write_data.aux = &proc_aux;
not_canceled = 1;
open_active_file ();
begin_func (func_aux);
if (vfm_source != NULL)
- vfm_source->class->read (vfm_source, process_active_file_write_case,
+ vfm_source->class->read (vfm_source, proc_aux.trns_case,
+ process_active_file_write_case,
&process_active_write_data);
end_func (func_aux);
close_active_file (&process_active_write_data);
}
-/* Pass the current case to casefunc. */
+/* Pass the current case to case_func. */
static int
-process_active_file_write_case (struct write_case_data *data)
+process_active_file_write_case (struct write_case_data *wc_data)
{
- /* Index of current transformation. */
- int cur_trns;
+ struct procedure_aux_data *proc_aux = wc_data->aux;
+ int cur_trns; /* Index of current transformation. */
- for (cur_trns = f_trns ; cur_trns != temp_trns; )
+ for (cur_trns = f_trns; cur_trns != temp_trns; )
{
int code;
- code = t_trns[cur_trns]->proc (t_trns[cur_trns], temp_case,
+ code = t_trns[cur_trns]->proc (t_trns[cur_trns], proc_aux->trns_case,
case_count + 1);
switch (code)
{
}
if (n_lag)
- lag_case ();
+ lag_case (proc_aux->trns_case);
/* Call the procedure if FILTER and PROCESS IF don't prohibit it. */
- if (not_canceled && !exclude_this_case (case_count + 1))
- not_canceled = data->proc_func (temp_case, data->func_aux);
+ if (not_canceled && !exclude_this_case (proc_aux->trns_case, case_count + 1))
+ not_canceled = wc_data->proc_func (proc_aux->trns_case, wc_data->func_aux);
case_count++;
done:
- clear_temp_case ();
+ clear_case (proc_aux->trns_case);
return 1;
}
-/* Write temp_case to the active file. */
+/* Write the given case to the active file. */
void
-process_active_file_output_case (void)
+process_active_file_output_case (const struct ccase *c)
{
- vfm_sink->class->write (vfm_sink, temp_case);
+ vfm_sink->class->write (vfm_sink, c);
+}
+\f
+/* Creates and returns a case, initializing it from the vectors
+ that say which `value's need to be initialized just once, and
+ which ones need to be re-initialized before every case. */
+static struct ccase *
+create_trns_case (struct dictionary *dict)
+{
+ struct ccase *c = xmalloc (dict_get_case_size (dict));
+ size_t var_cnt = dict_get_var_cnt (dict);
+ size_t i;
+
+ for (i = 0; i < var_cnt; i++)
+ {
+ struct variable *v = dict_get_var (dict, i);
+
+ if (v->type == NUMERIC)
+ {
+ if (v->reinit)
+ c->data[v->fv].f = 0.0;
+ else
+ c->data[v->fv].f = SYSMIS;
+ }
+ else
+ memset (c->data[v->fv].s, ' ', v->width);
+ }
+ return c;
}
\f
/* Opening the active file. */
static void
prepare_for_writing (void)
{
- /* FIXME: If ALL the conditions listed below hold true, then the
- replacement active file is guaranteed to be identical to the
- original active file:
-
- 1. TEMPORARY was the first transformation, OR, there were no
- transformations at all.
-
- 2. Input is not coming from an input program.
-
- 3. Compaction is not necessary.
-
- So, in this case, we shouldn't have to replace the active
- file--it's just a waste of time and space. */
-
if (vfm_sink == NULL)
{
if (workspace_overflow)
if (vfm_sink->class->open != NULL)
vfm_sink->class->open (vfm_sink);
-}
-
-/* Prepares the temporary case and compaction case. */
-static void
-make_temp_case (void)
-{
- temp_case = xmalloc (dict_get_case_size (default_dict));
if (compaction_necessary)
compaction_case = xmalloc (sizeof (struct ccase)
+ sizeof (union value) * (compaction_nval - 1));
+
}
#if DEBUGGING
}
#endif
-/* Initializes temp_case from the vectors that say which `value's
- need to be initialized just once, and which ones need to be
- re-initialized before every case. */
-static void
-vector_initialization (void)
-{
- size_t var_cnt = dict_get_var_cnt (default_dict);
- size_t i;
-
- for (i = 0; i < var_cnt; i++)
- {
- struct variable *v = dict_get_var (default_dict, i);
-
- if (v->type == NUMERIC)
- {
- if (v->reinit)
- temp_case->data[v->fv].f = 0.0;
- else
- temp_case->data[v->fv].f = SYSMIS;
- }
- else
- memset (temp_case->data[v->fv].s, ' ', v->width);
- }
-}
-
/* Sets all the lag-related variables based on value of n_lag. */
static void
setup_lag (void)
/* The rest. */
prepare_for_writing ();
arrange_compaction ();
- make_temp_case ();
- vector_initialization ();
discard_ctl_stack ();
setup_lag ();
}
free (vfm_source);
}
- vfm_source = vfm_sink->class->make_source (vfm_sink);
+ if (vfm_sink->class->make_source != NULL)
+ vfm_source = vfm_sink->class->make_source (vfm_sink);
+ else
+ vfm_source = NULL;
/* Old data sink is gone now. */
free (vfm_sink);
cancel_temporary ();
/* Free temporary cases. */
- free (temp_case);
- temp_case = NULL;
-
free (compaction_case);
compaction_case = NULL;
}
}
-/* Writes temp_case to the disk sink. */
+/* Writes case C to the disk sink. */
static void
-disk_sink_write (struct case_sink *sink, struct ccase *c)
+disk_sink_write (struct case_sink *sink, const struct ccase *c)
{
struct disk_stream_info *info = sink->aux;
- union value *src_case;
+ const union value *src_case;
if (compaction_necessary)
{
err_failure ();
}
- return create_case_source (&disk_source_class, info);
+ return create_case_source (&disk_source_class, default_dict, info);
}
/* Disk sink. */
write_case(). */
static void
disk_source_read (struct case_source *source,
+ struct ccase *c,
write_case_func *write_case, write_case_data wc_data)
{
struct disk_stream_info *info = source->aux;
for (i = 0; i < info->case_cnt; i++)
{
- if (!fread (temp_case, info->case_size, 1, info->file))
+ if (!fread (c, info->case_size, 1, info->file))
{
msg (ME, _("An error occurred while attempting to read from "
"a temporary file created for the active file: %s."),
strerror (errno));
err_failure ();
- return;
+ break;
}
if (!write_case (wc_data))
- return;
+ break;
}
}
struct case_list *cases; /* List of cases. */
};
+/* Creates the SINK memory sink. */
static void
memory_sink_create (struct case_sink *sink)
{
info->head = info->tail = NULL;
}
+/* Writes case C to memory sink SINK. */
static void
-memory_sink_write (struct case_sink *sink, struct ccase *c)
+memory_sink_write (struct case_sink *sink, const struct ccase *c)
{
struct memory_sink_info *info = sink->aux;
size_t case_size;
free (sink_info);
- return create_case_source (&memory_source_class, source_info);
+ return create_case_source (&memory_source_class,
+ default_dict, source_info);
}
const struct case_sink_class memory_sink_class =
/* Reads the case stream from memory and passes it to write_case(). */
static void
memory_source_read (struct case_source *source,
+ struct ccase *c,
write_case_func *write_case, write_case_data wc_data)
{
struct memory_source_info *info = source->aux;
while (info->cases != NULL)
{
struct case_list *iter = info->cases;
+ memcpy (c, &iter->c, info->case_size);
+ if (!write_case (wc_data))
+ break;
+
info->cases = iter->next;
- memcpy (temp_case, &iter->c, info->case_size);
free (iter);
-
- if (!write_case (wc_data))
- return;
}
}
free (info);
}
+/* Returns the list of cases in memory source SOURCE. */
struct case_list *
memory_source_get_cases (const struct case_source *source)
{
return info->cases;
}
+/* Sets the list of cases in memory source SOURCE to CASES. */
void
memory_source_set_cases (const struct case_source *source,
struct case_list *cases)
memory_source_destroy,
};
\f
-/* Add temp_case to the lag queue. */
+/* Add C to the lag queue. */
static void
-lag_case (void)
+lag_case (const struct ccase *c)
{
if (lag_count < n_lag)
lag_count++;
- memcpy (lag_queue[lag_head], temp_case,
- dict_get_case_size (temp_dict));
+ memcpy (lag_queue[lag_head], c, dict_get_case_size (temp_dict));
if (++lag_head >= n_lag)
lag_head = 0;
}
}
}
-/* Transforms temp_case and writes it to the replacement active file
- if advisable. Returns nonzero if more cases can be accepted, zero
- otherwise. Do not call this function again after it has returned
- zero once. */
+/* Transforms trns_case and writes it to the replacement active
+ file if advisable. Returns nonzero if more cases can be
+ accepted, zero otherwise. Do not call this function again
+ after it has returned zero once. */
int
procedure_write_case (write_case_data wc_data)
{
int case_limit;
if (n_lag)
- lag_case ();
+ lag_case (proc_aux->trns_case);
- vfm_sink->class->write (vfm_sink, temp_case);
+ vfm_sink->class->write (vfm_sink, proc_aux->trns_case);
proc_aux->cases_written++;
case_limit = dict_get_case_limit (default_dict);
{
int code;
- code = t_trns[cur_trns]->proc (t_trns[cur_trns], temp_case,
+ code = t_trns[cur_trns]->proc (t_trns[cur_trns], proc_aux->trns_case,
proc_aux->cases_written + 1);
switch (code)
{
/* Call the procedure if there is one and FILTER and PROCESS IF
don't prohibit it. */
if (wc_data->proc_func != NULL
- && !exclude_this_case (proc_aux->cases_written + 1))
- wc_data->proc_func (temp_case, wc_data->func_aux);
+ && !exclude_this_case (proc_aux->trns_case, proc_aux->cases_written + 1))
+ wc_data->proc_func (proc_aux->trns_case, wc_data->func_aux);
case_count++;
done:
- clear_temp_case ();
+ clear_case (proc_aux->trns_case);
/* Return previously determined value. */
return more_cases;
}
-/* Clears the variables in the temporary case that need to be
- cleared between processing cases. */
+/* Clears the variables in C that need to be cleared between
+ processing cases. */
static void
-clear_temp_case (void)
+clear_case (struct ccase *c)
{
/* FIXME? This is linear in the number of variables, but
doesn't need to be, so it's an easy optimization target. */
if (v->init && v->reinit)
{
if (v->type == NUMERIC)
- temp_case->data[v->fv].f = SYSMIS;
+ c->data[v->fv].f = SYSMIS;
else
- memset (temp_case->data[v->fv].s, ' ', v->width);
+ memset (c->data[v->fv].s, ' ', v->width);
}
}
}
-/* Returns nonzero if this case (numbered CASE_NUM) should be
+/* Returns nonzero if case C with case number CASE_NUM should be
exclude as specified on FILTER or PROCESS IF, otherwise
zero. */
static int
-exclude_this_case (int case_num)
+exclude_this_case (const struct ccase *c, int case_num)
{
/* FILTER. */
struct variable *filter_var = dict_get_filter (default_dict);
if (filter_var != NULL)
{
- double f = temp_case->data[filter_var->fv].f;
+ double f = c->data[filter_var->fv].f;
if (f == 0.0 || f == SYSMIS || is_num_user_missing (f, filter_var))
return 1;
}
/* PROCESS IF. */
if (process_if_expr != NULL
- && expr_evaluate (process_if_expr, temp_case, case_num, NULL) != 1.0)
+ && expr_evaluate (process_if_expr, c, case_num, NULL) != 1.0)
return 1;
return 0;
dict_compact_values (default_dict);
}
+/* Creates a case source with class CLASS and auxiliary data AUX
+ and based on dictionary DICT. */
struct case_source *
-create_case_source (const struct case_source_class *class, void *aux)
+create_case_source (const struct case_source_class *class,
+ const struct dictionary *dict,
+ void *aux)
{
struct case_source *source = xmalloc (sizeof *source);
source->class = class;
+ source->value_cnt = dict_get_next_value_idx (dict);
source->aux = aux;
return source;
}
+/* Returns nonzero if a case source is "complex". */
int
case_source_is_complex (const struct case_source *source)
{
|| source->class == &file_type_source_class);
}
+/* Returns nonzero if CLASS is the class of SOURCE. */
int
case_source_is_class (const struct case_source *source,
const struct case_source_class *class)
return source != NULL && source->class == class;
}
+/* Creates a case sink with class CLASS and auxiliary data
+ AUX. */
struct case_sink *
create_case_sink (const struct case_sink_class *class, void *aux)
{
sink->aux = aux;
return sink;
}
-
/* This is the time at which vfm was last invoked. */
extern time_t last_vfm_invocation;
-/* This is the case that is to be filled in by input programs. */
-extern struct ccase *temp_case;
-
+struct ccase;
typedef struct write_case_data *write_case_data;
typedef int write_case_func (write_case_data);
\f
struct case_source
{
const struct case_source_class *class; /* Class. */
- void *aux; /* Auxiliary data. */
+ size_t value_cnt; /* Number of `union value's in case. */
+ void *aux; /* Auxiliary data. */
};
/* A case source class. */
WRITE_CASE, if known, or -1 otherwise. */
int (*count) (const struct case_source *);
- /* Reads all the cases and calls WRITE_CASE passing the given
- AUX data for each one. */
- void (*read) (struct case_source *, write_case_func *, write_case_data);
+ /* Reads the cases one by one into C and for each one calls
+ WRITE_CASE passing the given AUX data. */
+ void (*read) (struct case_source *,
+ struct ccase *c,
+ write_case_func *write_case, write_case_data aux);
/* Destroys the source. */
void (*destroy) (struct case_source *);
extern const struct case_source_class import_source_class;
extern const struct case_source_class sort_source_class;
+struct dictionary;
struct case_source *create_case_source (const struct case_source_class *,
+ const struct dictionary *,
void *);
int case_source_is_complex (const struct case_source *);
int case_source_is_class (const struct case_source *,
void (*open) (struct case_sink *);
/* Writes a case to the sink. */
- void (*write) (struct case_sink *, struct ccase *);
+ void (*write) (struct case_sink *, const struct ccase *);
/* Closes and destroys the sink. */
void (*destroy) (struct case_sink *);
/* Number of cases to lag. */
extern int n_lag;
-void procedure (void (*beginfunc) (void *aux),
- int (*procfunc) (struct ccase *curcase, void *aux),
- void (*endfunc) (void *aux),
+void procedure (void (*begin_func) (void *aux),
+ int (*proc_func) (struct ccase *, void *aux),
+ void (*end_func) (void *aux),
void *aux);
struct ccase *lagged_case (int n_before);
void compact_case (struct ccase *dest, const struct ccase *src);
void write_active_file_to_disk (void);
-void process_active_file (void (*beginfunc) (void *),
- int (*casefunc) (struct ccase *curcase, void *),
- void (*endfunc) (void *),
+void process_active_file (void (*begin_func) (void *),
+ int (*casefunc) (struct ccase *, void *),
+ void (*end_func) (void *),
void *aux);
-void process_active_file_output_case (void);
+void process_active_file_output_case (const struct ccase *);
#endif /* !vfm_h */
#include "str.h"
#include "var.h"
-/* Notes:
-
- If the weighting variable is deleted somehow (for instance by
- end-of-scope of TEMPORARY), weighting must be canceled.
-
- Scratch vars may not be used for weighting. */
-
/* WEIGHT transformation. */
struct weight_trns
{