/* PSPP - a program for statistical analysis.
- Copyright (C) 1997-9, 2000, 2006, 2007 Free Software Foundation, Inc.
+ Copyright (C) 1997-9, 2000, 2006, 2007, 2009 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
#include <ctype.h>
#include <errno.h>
-#include <fcntl.h>
#include <stdint.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <time.h>
-#include <unistd.h>
-#include <libpspp/alloc.h>
#include <libpspp/float-format.h>
#include <libpspp/integer-format.h>
-#include <libpspp/magic.h>
#include <libpspp/message.h>
#include <libpspp/misc.h>
#include <libpspp/str.h>
#include <libpspp/version.h>
+#include <data/attributes.h>
#include <data/case.h>
#include <data/casewriter-provider.h>
#include <data/casewriter.h>
#include <data/dictionary.h>
#include <data/file-handle-def.h>
+#include <data/file-name.h>
#include <data/format.h>
+#include <data/make-file.h>
#include <data/missing-values.h>
#include <data/settings.h>
#include <data/short-names.h>
#include "minmax.h"
#include "unlocked-io.h"
+#include "xalloc.h"
#include "gettext.h"
#define _(msgid) gettext (msgid)
+#define N_(msgid) (msgid)
/* Compression bias used by PSPP. Values between (1 -
COMPRESSION_BIAS) and (251 - COMPRESSION_BIAS) inclusive can be
struct sfm_writer
{
struct file_handle *fh; /* File handle. */
+ struct fh_lock *lock; /* Mutual exclusion for file. */
FILE *file; /* File stream. */
+ struct replace_file *rf; /* Ticket for replacing output file. */
bool compress; /* 1=compressed, 0=not compressed. */
casenumber case_cnt; /* Number of cases written so far. */
for long string variables. */
};
-static struct casewriter_class sys_file_casewriter_class;
+static const struct casewriter_class sys_file_casewriter_class;
static void write_header (struct sfm_writer *, const struct dictionary *);
static void write_variable (struct sfm_writer *, const struct variable *);
static void write_longvar_table (struct sfm_writer *w,
const struct dictionary *dict);
+static void write_encoding_record (struct sfm_writer *w,
+ const struct dictionary *);
+
static void write_vls_length_table (struct sfm_writer *w,
const struct dictionary *dict);
static void write_documents (struct sfm_writer *, const struct dictionary *);
+static void write_data_file_attributes (struct sfm_writer *,
+ const struct dictionary *);
+static void write_variable_attributes (struct sfm_writer *,
+ const struct dictionary *);
+
static void write_int (struct sfm_writer *, int32_t);
static inline void convert_double_to_output_format (double, uint8_t[8]);
static void write_float (struct sfm_writer *, double);
static void write_spaces (struct sfm_writer *, size_t);
static void write_value (struct sfm_writer *, const union value *, int width);
-static void write_case_uncompressed (struct sfm_writer *, struct ccase *);
-static void write_case_compressed (struct sfm_writer *, struct ccase *);
+static void write_case_uncompressed (struct sfm_writer *,
+ const struct ccase *);
+static void write_case_compressed (struct sfm_writer *, const struct ccase *);
static void flush_compressed (struct sfm_writer *);
static void put_cmp_opcode (struct sfm_writer *, uint8_t);
static void put_cmp_number (struct sfm_writer *, double);
{
struct sfm_write_options opts;
opts.create_writeable = true;
- opts.compress = get_scompression ();
+ opts.compress = settings_get_scompression ();
opts.version = 3;
return opts;
}
sfm_open_writer (struct file_handle *fh, struct dictionary *d,
struct sfm_write_options opts)
{
- struct sfm_writer *w = NULL;
+ struct sfm_writer *w;
mode_t mode;
- int fd;
int idx;
int i;
opts.version = 3;
}
- /* Create file. */
- mode = S_IRUSR | S_IRGRP | S_IROTH;
- if (opts.create_writeable)
- mode |= S_IWUSR | S_IWGRP | S_IWOTH;
- fd = open (fh_get_file_name (fh), O_WRONLY | O_CREAT | O_TRUNC, mode);
- if (fd < 0)
- goto open_error;
-
- /* Open file handle. */
- if (!fh_open (fh, FH_REF_FILE, "system file", "we"))
- goto error;
-
/* Create and initialize writer. */
w = xmalloc (sizeof *w);
- w->fh = fh;
- w->file = fdopen (fd, "w");
+ w->fh = fh_ref (fh);
+ w->lock = NULL;
+ w->file = NULL;
+ w->rf = NULL;
w->compress = opts.compress;
w->case_cnt = 0;
w->segment_cnt = sfm_dictionary_to_sfm_vars (d, &w->sfm_vars,
&w->sfm_var_cnt);
- /* Check that file create succeeded. */
- if (w->file == NULL)
+ /* Open file handle as an exclusive writer. */
+ /* TRANSLATORS: this fragment will be interpolated into
+ messages in fh_lock() that identify types of files. */
+ w->lock = fh_lock (fh, FH_REF_FILE, N_("system file"), FH_ACC_WRITE, true);
+ if (w->lock == NULL)
+ goto error;
+
+ /* Create the file on disk. */
+ mode = S_IRUSR | S_IRGRP | S_IROTH;
+ if (opts.create_writeable)
+ mode |= S_IWUSR | S_IWGRP | S_IWOTH;
+ w->rf = replace_file_start (fh_get_file_name (fh), "wb", mode,
+ &w->file, NULL);
+ if (w->rf == NULL)
{
- close (fd);
- goto open_error;
+ msg (ME, _("Error opening \"%s\" for writing as a system file: %s."),
+ fh_get_file_name (fh), strerror (errno));
+ goto error;
}
/* Write the file header. */
write_vls_length_table (w, d);
+ if (attrset_count (dict_get_attributes (d)))
+ write_data_file_attributes (w, d);
+ write_variable_attributes (w, d);
+
+ write_encoding_record (w, d);
+
/* Write end-of-headers record. */
write_int (w, 999);
write_int (w, 0);
if (write_error (w))
- goto error;
+ {
+ close_writer (w);
+ return NULL;
+ }
- return casewriter_create (&sys_file_casewriter_class, w);
+ return casewriter_create (dict_get_next_value_idx (d),
+ &sys_file_casewriter_class, w);
- error:
+error:
close_writer (w);
return NULL;
-
- open_error:
- msg (ME, _("Error opening \"%s\" for writing as a system file: %s."),
- fh_get_file_name (fh), strerror (errno));
- goto error;
}
/* Returns value of X truncated to two least-significant digits. */
}
else
{
- static const char *month_name[12] =
+ static const char *const month_name[12] =
{
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
/* Number of missing values. If there is a range, then the
range counts as 2 missing values and causes the number to be
negated. */
- write_int (w, mv_has_range (mv) ? 2 - mv_n_values (mv) : mv_n_values (mv));
+ write_int (w, mv_has_range (mv) ? -2 - mv_n_values (mv) : mv_n_values (mv));
/* Print and write formats. */
write_format (w, *var_get_print_format (v), seg0_width);
if (mv_has_range (mv))
{
double x, y;
- mv_peek_range (mv, &x, &y);
+ mv_get_range (mv, &x, &y);
write_float (w, x);
write_float (w, y);
}
for (i = 0; i < mv_n_values (mv); i++)
{
union value value;
- mv_peek_value (mv, &value, i);
+ mv_get_value (mv, &value, i);
write_value (w, &value, seg0_width);
}
write_bytes (w, dict_get_documents (d), line_cnt * DOC_LINE_LENGTH);
}
+static void
+put_attrset (struct string *string, const struct attrset *attrs)
+{
+ const struct attribute *attr;
+ struct attrset_iterator i;
+
+ for (attr = attrset_first (attrs, &i); attr != NULL;
+ attr = attrset_next (attrs, &i))
+ {
+ size_t n_values = attribute_get_n_values (attr);
+ size_t j;
+
+ ds_put_cstr (string, attribute_get_name (attr));
+ ds_put_char (string, '(');
+ for (j = 0; j < n_values; j++)
+ ds_put_format (string, "'%s'\n", attribute_get_value (attr, j));
+ ds_put_char (string, ')');
+ }
+}
+
+static void
+write_attribute_record (struct sfm_writer *w, const struct string *content,
+ int subtype)
+{
+ write_int (w, 7);
+ write_int (w, subtype);
+ write_int (w, 1);
+ write_int (w, ds_length (content));
+ write_bytes (w, ds_data (content), ds_length (content));
+}
+
+static void
+write_data_file_attributes (struct sfm_writer *w,
+ const struct dictionary *d)
+{
+ struct string s = DS_EMPTY_INITIALIZER;
+ put_attrset (&s, dict_get_attributes (d));
+ write_attribute_record (w, &s, 17);
+ ds_destroy (&s);
+}
+
+static void
+write_variable_attributes (struct sfm_writer *w, const struct dictionary *d)
+{
+ struct string s = DS_EMPTY_INITIALIZER;
+ size_t n_vars = dict_get_var_cnt (d);
+ size_t n_attrsets = 0;
+ size_t i;
+
+ for (i = 0; i < n_vars; i++)
+ {
+ struct variable *v = dict_get_var (d, i);
+ struct attrset *attrs = var_get_attributes (v);
+ if (attrset_count (attrs))
+ {
+ if (n_attrsets++)
+ ds_put_char (&s, '/');
+ ds_put_format (&s, "%s:", var_get_short_name (v, 0));
+ put_attrset (&s, attrs);
+ }
+ }
+ if (n_attrsets)
+ write_attribute_record (w, &s, 18);
+ ds_destroy (&s);
+}
+
/* Write the alignment, width and scale values. */
static void
write_variable_display_parameters (struct sfm_writer *w,
ds_destroy (&map);
}
+
+static void
+write_encoding_record (struct sfm_writer *w,
+ const struct dictionary *d)
+{
+ const char *enc = dict_get_encoding (d);
+
+ if ( NULL == enc)
+ return;
+
+ write_int (w, 7); /* Record type. */
+ write_int (w, 20); /* Record subtype. */
+ write_int (w, 1); /* Data item (char) size. */
+ write_int (w, strlen (enc)); /* Number of data items. */
+ write_string (w, enc, strlen (enc));
+}
+
+
/* Writes the long variable name table. */
static void
write_longvar_table (struct sfm_writer *w, const struct dictionary *dict)
if (ferror (w->file))
{
casewriter_force_error (writer);
- case_destroy (c);
+ case_unref (c);
return;
}
else
write_case_compressed (w, c);
- case_destroy (c);
+ case_unref (c);
}
/* Destroys system file writer W. */
if (!ok)
msg (ME, _("An I/O error occurred writing system file \"%s\"."),
fh_get_file_name (w->fh));
+
+ if (ok ? !replace_file_commit (w->rf) : !replace_file_abort (w->rf))
+ ok = false;
}
- fh_close (w->fh, "system file", "we");
+ fh_unlock (w->lock);
+ fh_unref (w->fh);
free (w->sfm_vars);
free (w);
}
/* System file writer casewriter class. */
-static struct casewriter_class sys_file_casewriter_class =
+static const struct casewriter_class sys_file_casewriter_class =
{
sys_file_casewriter_write,
sys_file_casewriter_destroy,
\f
/* Writes case C to system file W, without compressing it. */
static void
-write_case_uncompressed (struct sfm_writer *w, struct ccase *c)
+write_case_uncompressed (struct sfm_writer *w, const struct ccase *c)
{
size_t i;
/* Writes case C to system file W, with compression. */
static void
-write_case_compressed (struct sfm_writer *w, struct ccase *c)
+write_case_compressed (struct sfm_writer *w, const struct ccase *c)
{
size_t i;
}
/* Writes null-terminated STRING in a field of the given WIDTH to
- W. If WIDTH is longer than WIDTH, it is truncated; if WIDTH
+ W. If STRING is longer than WIDTH, it is truncated; if WIDTH
is narrowed, it is padded on the right with spaces. */
static void
write_string (struct sfm_writer *w, const char *string, size_t width)