struct u8_line *held;
};
+struct save_file
+ {
+ struct file_handle *file;
+
+ /* Parameters from parsing the first SAVE command for 'file'. */
+ struct string_array variables;
+ struct matrix_expr *names;
+ struct stringi_set strings;
+
+ /* Results from the first attempt to open this save_file. */
+ bool error;
+ struct casewriter *writer;
+ };
+
struct matrix_state
{
struct dataset *dataset;
struct lexer *lexer;
struct hmap vars;
bool in_loop;
- struct file_handle *prev_save_outfile;
struct msave_common *common;
struct file_handle *prev_read_file;
struct file_handle *prev_write_file;
struct write_file **write_files;
size_t n_write_files;
+
+ struct file_handle *prev_save_file;
+ struct save_file **save_files;
+ size_t n_save_files;
};
static struct matrix_var *
struct save_command
{
struct matrix_expr *expression;
- struct file_handle *outfile;
- struct string_array *variables;
- struct matrix_expr *names;
- struct stringi_set strings;
+ struct save_file *sf;
}
save;
int c1, c2;
enum fmt_type format;
int w;
- //int d;
bool symmetric;
bool reread;
}
}
}
\f
+static struct save_file *
+save_file_create (struct matrix_state *s, struct file_handle *fh,
+ struct string_array *variables,
+ struct matrix_expr *names,
+ struct stringi_set *strings)
+{
+ for (size_t i = 0; i < s->n_save_files; i++)
+ {
+ struct save_file *sf = s->save_files[i];
+ if (sf->file == fh)
+ {
+ fh_unref (fh);
+
+ string_array_destroy (variables);
+ matrix_expr_destroy (names);
+ stringi_set_destroy (strings);
+
+ return sf;
+ }
+ }
+
+ struct save_file *sf = xmalloc (sizeof *sf);
+ *sf = (struct save_file) {
+ .file = fh,
+ .variables = *variables,
+ .names = names,
+ .strings = STRINGI_SET_INITIALIZER (sf->strings),
+ };
+
+ stringi_set_swap (&sf->strings, strings);
+ stringi_set_destroy (strings);
+
+ s->save_files = xrealloc (s->save_files,
+ (s->n_save_files + 1) * sizeof *s->save_files);
+ s->save_files[s->n_save_files++] = sf;
+ return sf;
+}
+
+static struct casewriter *
+save_file_open (struct save_file *sf, gsl_matrix *m)
+{
+ if (sf->writer || sf->error)
+ {
+ if (sf->writer)
+ {
+ size_t n_variables = caseproto_get_n_widths (
+ casewriter_get_proto (sf->writer));
+ if (m->size2 != n_variables)
+ {
+ msg (ME, _("The first SAVE to %s within this matrix program "
+ "had %zu columns, so a %zuĂ—%zu matrix cannot be "
+ "saved to it."),
+ fh_get_name (sf->file), n_variables, m->size1, m->size2);
+ return NULL;
+ }
+ }
+ return sf->writer;
+ }
+
+ bool ok = true;
+ struct dictionary *dict = dict_create (get_default_encoding ());
+
+ /* Fill 'names' with user-specified names if there were any, either from
+ sf->variables or sf->names. */
+ struct string_array names = { .n = 0 };
+ if (sf->variables.n)
+ string_array_clone (&names, &sf->variables);
+ else if (sf->names)
+ {
+ gsl_matrix *nm = matrix_expr_evaluate (sf->names);
+ if (nm && is_vector (nm))
+ {
+ gsl_vector nv = to_vector (nm);
+ for (size_t i = 0; i < nv.size; i++)
+ {
+ char *name = trimmed_string (gsl_vector_get (&nv, i));
+ if (dict_id_is_valid (dict, name, true))
+ string_array_append_nocopy (&names, name);
+ else
+ ok = false;
+ }
+ }
+ gsl_matrix_free (nm);
+ }
+
+ struct stringi_set strings;
+ stringi_set_clone (&strings, &sf->strings);
+
+ for (size_t i = 0; dict_get_var_cnt (dict) < m->size2; i++)
+ {
+ char tmp_name[64];
+ const char *name;
+ if (i < names.n)
+ name = names.strings[i];
+ else
+ {
+ snprintf (tmp_name, sizeof tmp_name, "COL%zu", i + 1);
+ name = tmp_name;
+ }
+
+ int width = stringi_set_delete (&strings, name) ? 8 : 0;
+ struct variable *var = dict_create_var (dict, name, width);
+ if (!var)
+ {
+ msg (ME, _("Duplicate variable name %s in SAVE statement."),
+ name);
+ ok = false;
+ }
+ }
+
+ if (!stringi_set_is_empty (&strings))
+ {
+ const char *example = stringi_set_node_get_string (
+ stringi_set_first (&strings));
+ msg (ME, ngettext ("STRINGS specified a variable %s, but no variable "
+ "with that name was found on SAVE.",
+ "STRINGS specified %2$zu variables, including %1$s, "
+ "whose names were not found on SAVE.",
+ stringi_set_count (&strings)),
+ example, stringi_set_count (&strings));
+ ok = false;
+ }
+ stringi_set_destroy (&strings);
+ string_array_destroy (&names);
+
+ if (!ok)
+ {
+ dict_unref (dict);
+ sf->error = true;
+ return NULL;
+ }
+
+ sf->writer = any_writer_open (sf->file, dict);
+ dict_unref (dict);
+ if (!sf->writer)
+ {
+ sf->error = true;
+ return NULL;
+ }
+ return sf->writer;
+}
+
+static void
+save_file_destroy (struct save_file *sf)
+{
+ if (sf)
+ {
+ fh_unref (sf->file);
+ string_array_destroy (&sf->variables);
+ matrix_expr_destroy (sf->names);
+ stringi_set_destroy (&sf->strings);
+ casewriter_destroy (sf->writer);
+ free (sf);
+ }
+}
+
static struct matrix_cmd *
matrix_parse_save (struct matrix_state *s)
{
struct matrix_cmd *cmd = xmalloc (sizeof *cmd);
*cmd = (struct matrix_cmd) {
.type = MCMD_SAVE,
- .save = {
- .strings = STRINGI_SET_INITIALIZER (cmd->save.strings)
- }
+ .save = { .expression = NULL },
};
+ struct file_handle *fh = NULL;
struct save_command *save = &cmd->save;
+
+ struct string_array variables = STRING_ARRAY_INITIALIZER;
+ struct matrix_expr *names = NULL;
+ struct stringi_set strings = STRINGI_SET_INITIALIZER (strings);
+
save->expression = matrix_parse_exp (s);
if (!save->expression)
goto error;
{
lex_match (s->lexer, T_EQUALS);
- fh_unref (save->outfile);
- save->outfile = (lex_match (s->lexer, T_ASTERISK)
- ? fh_inline_file ()
- : fh_parse (s->lexer, FH_REF_FILE, s->session));
- if (!save->outfile)
+ fh_unref (fh);
+ fh = (lex_match (s->lexer, T_ASTERISK)
+ ? fh_inline_file ()
+ : fh_parse (s->lexer, FH_REF_FILE, s->session));
+ if (!fh)
goto error;
}
else if (lex_match_id (s->lexer, "VARIABLES"))
if (!ok)
goto error;
- string_array_destroy (save->variables);
- if (!save->variables)
- save->variables = xmalloc (sizeof *save->variables);
- *save->variables = (struct string_array) {
+ string_array_clear (&variables);
+ variables = (struct string_array) {
.strings = names,
.n = n,
.allocated = n,
else if (lex_match_id (s->lexer, "NAMES"))
{
lex_match (s->lexer, T_EQUALS);
- matrix_expr_destroy (save->names);
- save->names = matrix_parse_exp (s);
- if (!save->names)
+ matrix_expr_destroy (names);
+ names = matrix_parse_exp (s);
+ if (!names)
goto error;
}
else if (lex_match_id (s->lexer, "STRINGS"))
lex_match (s->lexer, T_EQUALS);
while (lex_token (s->lexer) == T_ID)
{
- stringi_set_insert (&save->strings, lex_tokcstr (s->lexer));
+ stringi_set_insert (&strings, lex_tokcstr (s->lexer));
lex_get (s->lexer);
if (!lex_match (s->lexer, T_COMMA))
break;
}
}
- if (!save->outfile)
+ if (!fh)
{
- if (s->prev_save_outfile)
- save->outfile = fh_ref (s->prev_save_outfile);
+ if (s->prev_save_file)
+ fh = fh_ref (s->prev_save_file);
else
{
lex_sbc_missing ("OUTFILE");
goto error;
}
}
- fh_unref (s->prev_save_outfile);
- s->prev_save_outfile = fh_ref (save->outfile);
+ fh_unref (s->prev_save_file);
+ s->prev_save_file = fh_ref (fh);
- if (save->variables && save->names)
+ if (variables.n && names)
{
msg (SW, _("VARIABLES and NAMES both specified; ignoring NAMES."));
- matrix_expr_destroy (save->names);
- save->names = NULL;
+ matrix_expr_destroy (names);
+ names = NULL;
}
+ save->sf = save_file_create (s, fh, &variables, names, &strings);
+ fh = NULL;
+
return cmd;
error:
+ string_array_destroy (&variables);
+ matrix_expr_destroy (names);
+ stringi_set_destroy (&strings);
+ fh_unref (fh);
matrix_cmd_destroy (cmd);
return NULL;
}
static void
matrix_cmd_execute_save (const struct save_command *save)
{
- assert (save->outfile != fh_inline_file ()); /* XXX not yet implemented */
-
gsl_matrix *m = matrix_expr_evaluate (save->expression);
if (!m)
return;
- bool ok = true;
- struct dictionary *dict = dict_create (get_default_encoding ());
- struct string_array names = { .n = 0 };
- if (save->variables)
- string_array_clone (&names, save->variables);
- else if (save->names)
- {
- gsl_matrix *nm = matrix_expr_evaluate (save->names);
- if (nm && is_vector (nm))
- {
- gsl_vector nv = to_vector (nm);
- for (size_t i = 0; i < nv.size; i++)
- {
- char *name = trimmed_string (gsl_vector_get (&nv, i));
- if (dict_id_is_valid (dict, name, true))
- string_array_append_nocopy (&names, name);
- else
- ok = false;
- }
- }
- }
-
- struct stringi_set strings;
- stringi_set_clone (&strings, &save->strings);
-
- for (size_t i = 0; dict_get_var_cnt (dict) < m->size2; i++)
- {
- char tmp_name[64];
- const char *name;
- if (i < names.n)
- name = names.strings[i];
- else
- {
- snprintf (tmp_name, sizeof tmp_name, "COL%zu", i + 1);
- name = tmp_name;
- }
-
- int width = stringi_set_delete (&strings, name) ? 8 : 0;
- struct variable *var = dict_create_var (dict, name, width);
- if (!var)
- {
- msg (SE, _("Duplicate variable name %s in SAVE statement."),
- name);
- ok = false;
- }
- }
-
- if (!stringi_set_is_empty (&strings))
- {
- const char *example = stringi_set_node_get_string (
- stringi_set_first (&strings));
- msg (SE, ngettext ("STRINGS specified a variable %s, but no variable "
- "with that name was found on SAVE.",
- "STRINGS specified %2$zu variables, including %1$s, "
- "whose names were not found on SAVE.",
- stringi_set_count (&strings)),
- example, stringi_set_count (&strings));
- ok = false;
- }
- stringi_set_destroy (&strings);
- string_array_destroy (&names);
-
- if (!ok)
- {
- gsl_matrix_free (m);
- dict_unref (dict);
- return;
- }
-
- struct casewriter *writer = any_writer_open (save->outfile, dict);
+ struct casewriter *writer = save_file_open (save->sf, m);
if (!writer)
{
gsl_matrix_free (m);
- dict_unref (dict);
return;
}
- const struct caseproto *proto = dict_get_proto (dict);
+ const struct caseproto *proto = casewriter_get_proto (writer);
for (size_t y = 0; y < m->size1; y++)
{
struct ccase *c = case_create (proto);
}
casewriter_write (writer, c);
}
- casewriter_destroy (writer);
-
gsl_matrix_free (m);
- dict_unref (dict);
}
\f
static struct matrix_cmd *
case MCMD_SAVE:
matrix_expr_destroy (cmd->save.expression);
- fh_unref (cmd->save.outfile);
- string_array_destroy (cmd->save.variables);
- matrix_expr_destroy (cmd->save.names);
- stringi_set_destroy (&cmd->save.strings);
break;
case MCMD_READ:
free (var);
}
hmap_destroy (&state.vars);
- fh_unref (state.prev_save_outfile);
if (state.common)
{
dict_unref (state.common->dict);
for (size_t i = 0; i < state.n_write_files; i++)
write_file_destroy (state.write_files[i]);
free (state.write_files);
+ fh_unref (state.prev_save_file);
+ for (size_t i = 0; i < state.n_save_files; i++)
+ save_file_destroy (state.save_files[i]);
+ free (state.save_files);
return CMD_SUCCESS;
}