Don't pad variable labels written to system files.
[pspp-builds.git] / src / data / sys-file-writer.c
index b21053c216a54c5fa3181d161d6cd795ec5ed0d0..001b78f162a7d1724d73afba520f9066facbcfa4 100644 (file)
@@ -26,7 +26,6 @@
 #include <sys/stat.h>
 #include <time.h>
 
-#include <libpspp/alloc.h>
 #include <libpspp/float-format.h>
 #include <libpspp/integer-format.h>
 #include <libpspp/message.h>
@@ -41,6 +40,7 @@
 #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
@@ -62,7 +64,9 @@
 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. */
@@ -86,7 +90,7 @@ struct sfm_writer
                                    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 *);
@@ -132,7 +136,7 @@ sfm_writer_default_options (void)
 {
   struct sfm_write_options opts;
   opts.create_writeable = true;
-  opts.compress = get_scompression ();
+  opts.compress = settings_get_scompression ();
   opts.version = 3;
   return opts;
 }
@@ -148,9 +152,8 @@ struct casewriter *
 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;
-  FILE *file;
   int idx;
   int i;
 
@@ -162,27 +165,12 @@ sfm_open_writer (struct file_handle *fh, struct dictionary *d,
       opts.version = 3;
     }
 
-  /* Open file handle as an exclusive writer. */
-  if (!fh_open (fh, FH_REF_FILE, "system file", "we"))
-    return NULL;
-
-  /* Create the file on disk. */
-  mode = S_IRUSR | S_IRGRP | S_IROTH;
-  if (opts.create_writeable)
-    mode |= S_IWUSR | S_IWGRP | S_IWOTH;
-  file = create_stream (fh_get_file_name (fh), "w", mode);
-  if (file == NULL)
-    {
-      msg (ME, _("Error opening \"%s\" for writing as a system file: %s."),
-           fh_get_file_name (fh), strerror (errno));
-      fh_close (fh, "system file", "we");
-      return NULL;
-    }
-
   /* Create and initialize writer. */
   w = xmalloc (sizeof *w);
-  w->fh = fh;
-  w->file = file;
+  w->fh = fh_ref (fh);
+  w->lock = NULL;
+  w->file = NULL;
+  w->rf = NULL;
 
   w->compress = opts.compress;
   w->case_cnt = 0;
@@ -196,6 +184,26 @@ sfm_open_writer (struct file_handle *fh, struct dictionary *d,
   w->segment_cnt = sfm_dictionary_to_sfm_vars (d, &w->sfm_vars,
                                                &w->sfm_var_cnt);
 
+  /* 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)
+    {
+      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_header (w, d);
 
@@ -239,6 +247,10 @@ sfm_open_writer (struct file_handle *fh, struct dictionary *d,
 
   return casewriter_create (dict_get_next_value_idx (d),
                             &sys_file_casewriter_class, w);
+
+error:
+  close_writer (w);
+  return NULL;
 }
 
 /* Returns value of X truncated to two least-significant digits. */
@@ -321,7 +333,7 @@ write_header (struct sfm_writer *w, const struct dictionary *d)
     }
   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",
@@ -409,7 +421,7 @@ write_variable (struct sfm_writer *w, const struct variable *v)
   /* 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);
@@ -424,8 +436,9 @@ write_variable (struct sfm_writer *w, const struct variable *v)
   if (var_has_label (v))
     {
       const char *label = var_get_label (v);
-      size_t padded_len = ROUND_UP (MIN (strlen (label), 255), 4);
-      write_int (w, padded_len);
+      size_t label_len = MIN (strlen (label), 255);
+      size_t padded_len = ROUND_UP (label_len, 4);
+      write_int (w, label_len);
       write_string (w, label, padded_len);
     }
 
@@ -433,14 +446,14 @@ write_variable (struct sfm_writer *w, const struct variable *v)
   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);
     }
 
@@ -724,9 +737,13 @@ close_writer (struct sfm_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);
@@ -735,7 +752,7 @@ close_writer (struct sfm_writer *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,