Revamp SAVE, XSAVE, EXPORT. Add (or at least parse) all the
[pspp-builds.git] / src / sfm-write.c
index aedb5a8a4ad4d5ebe10b384912c10ccdb18d4603..4f0f9ec18a763abcfb1ca4b7cf8c548c637028b2 100644 (file)
@@ -14,8 +14,8 @@
 
    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
-   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
-   02111-1307, USA. */
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301, USA. */
 
 #include <config.h>
 #include "sfm-write.h"
@@ -24,6 +24,8 @@
 #include <stdlib.h>
 #include <ctype.h>
 #include <errno.h>
+#include <fcntl.h>
+#include <sys/stat.h>
 #include <time.h>
 #if HAVE_UNISTD_H
 #include <unistd.h>    /* Required by SunOS4. */
 #include "dictionary.h"
 #include "error.h"
 #include "file-handle.h"
-#include "getline.h"
+#include "getl.h"
 #include "hash.h"
 #include "magic.h"
 #include "misc.h"
+#include "settings.h"
+#include "stat-macros.h"
 #include "str.h"
 #include "value-labels.h"
 #include "var.h"
 #include "version.h"
 
+#include "gettext.h"
+#define _(msgid) gettext (msgid)
+
 #include "debug-print.h"
 
 /* Compression bias used by PSPP.  Values between (1 -
@@ -87,6 +94,14 @@ static int write_variable (struct sfm_writer *, struct variable *);
 static int write_value_labels (struct sfm_writer *,
                                struct variable *, int idx);
 static int write_rec_7_34 (struct sfm_writer *);
+
+static int write_longvar_table (struct sfm_writer *w, 
+                               const struct dictionary *dict);
+
+static int write_variable_display_parameters (struct sfm_writer *w, 
+                                             const struct dictionary *dict);
+
+
 static int write_documents (struct sfm_writer *, const struct dictionary *);
 static int does_dict_need_translation (const struct dictionary *);
 
@@ -96,30 +111,61 @@ var_flt64_cnt (const struct variable *v)
   return v->type == NUMERIC ? 1 : DIV_RND_UP (v->width, sizeof (flt64));
 }
 
+/* Returns default options for writing a system file. */
+struct sfm_write_options
+sfm_writer_default_options (void) 
+{
+  struct sfm_write_options opts;
+  opts.create_writeable = true;
+  opts.compress = get_scompression ();
+  opts.version = 3;
+  return opts;
+}
+
 /* Opens the system file designated by file handle FH for writing
-   cases from dictionary D.  If COMPRESS is nonzero, the
-   system file will be compressed.
+   cases from dictionary D according to the given OPTS.  If
+   COMPRESS is nonzero, the system file will be compressed.
 
    No reference to D is retained, so it may be modified or
-   destroyed at will after this function returns. */
+   destroyed at will after this function returns.  D is not
+   modified by this function, except to assign short names. */
 struct sfm_writer *
-sfm_open_writer (struct file_handle *fh,
-                 const struct dictionary *d, int compress)
+sfm_open_writer (struct file_handle *fh, struct dictionary *d,
+                 struct sfm_write_options opts)
 {
   struct sfm_writer *w = NULL;
+  mode_t mode;
+  int fd;
   int idx;
   int i;
 
+  /* Check version. */
+  if (opts.version != 2 && opts.version != 3) 
+    {
+      msg (ME, _("Unknown system file version %d. Treating as version %d."),
+           opts.version, 3);
+      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 (handle_get_filename (fh), O_WRONLY | O_CREAT | O_TRUNC, mode);
+  if (fd < 0) 
+    goto open_error;
+
+  /* Open file handle. */
   if (!fh_open (fh, "system file", "we"))
     goto error;
 
   /* Create and initialize writer. */
   w = xmalloc (sizeof *w);
   w->fh = fh;
-  w->file = fopen (handle_get_filename (fh), "wb");
+  w->file = fdopen (fd, "w");
 
   w->needs_translation = does_dict_need_translation (d);
-  w->compress = compress;
+  w->compress = opts.compress;
   w->case_cnt = 0;
   w->flt64_cnt = 0;
 
@@ -138,13 +184,10 @@ sfm_open_writer (struct file_handle *fh,
     }
 
   /* Check that file create succeeded. */
-  if (w->file == NULL)
+  if (w->file == NULL) 
     {
-      msg (ME, _("Error opening \"%s\" for writing "
-                 "as a system file: %s."),
-           handle_get_filename (w->fh), strerror (errno));
-      err_cond_fail ();
-      goto error;
+      close (fd);
+      goto open_error;
     }
 
   /* Write the file header. */
@@ -152,6 +195,7 @@ sfm_open_writer (struct file_handle *fh,
     goto error;
 
   /* Write basic variable info. */
+  dict_assign_short_names (d);
   for (i = 0; i < dict_get_var_cnt (d); i++)
     write_variable (w, dict_get_var (d, i));
 
@@ -167,10 +211,20 @@ sfm_open_writer (struct file_handle *fh,
 
   if (dict_get_documents (d) != NULL && !write_documents (w, d))
     goto error;
+
   if (!write_rec_7_34 (w))
     goto error;
 
-  /* Write record 999. */
+  if (!write_variable_display_parameters (w, d))
+    goto error;
+
+  if (opts.version >= 3) 
+    {
+      if (!write_longvar_table (w, d))
+       goto error;
+    }
+
+  /* Write end-of-headers record. */
   {
     struct
       {
@@ -200,6 +254,12 @@ sfm_open_writer (struct file_handle *fh,
  error:
   sfm_close_writer (w);
   return NULL;
+
+ open_error:
+  msg (ME, _("Error opening \"%s\" for writing as a system file: %s."),
+       handle_get_filename (w->fh), strerror (errno));
+  err_cond_fail ();
+  goto error;
 }
 
 static int
@@ -311,7 +371,7 @@ write_header (struct sfm_writer *w, const struct dictionary *d)
     if (label == NULL)
       label = "";
 
-    st_bare_pad_copy (hdr.file_label, label, sizeof hdr.file_label); 
+    buf_copy_str_rpad (hdr.file_label, sizeof hdr.file_label, label); 
   }
   
   memset (hdr.padding, 0, sizeof hdr.padding);
@@ -337,6 +397,7 @@ write_variable (struct sfm_writer *w, struct variable *v)
   struct sysfile_variable sv;
 
   /* Missing values. */
+  struct missing_values mv;
   flt64 m[3];           /* Missing value values. */
   int nm;               /* Number of missing values, possibly negative. */
 
@@ -344,60 +405,32 @@ write_variable (struct sfm_writer *w, struct variable *v)
   sv.type = v->width;
   sv.has_var_label = (v->label != NULL);
 
-  switch (v->miss_type)
+  mv_copy (&mv, &v->miss);
+  nm = 0;
+  if (mv_has_range (&mv)) 
+    {
+      double x, y;
+      mv_pop_range (&mv, &x, &y);
+      m[nm++] = x == LOWEST ? second_lowest_flt64 : x;
+      m[nm++] = y == HIGHEST ? FLT64_MAX : y;
+    }
+  while (mv_has_value (&mv))
     {
-    case MISSING_NONE:
-      nm = 0;
-      break;
-    case MISSING_1:
-    case MISSING_2:
-    case MISSING_3:
-      for (nm = 0; nm < v->miss_type; nm++)
-       m[nm] = v->missing[nm].f;
-      break;
-    case MISSING_RANGE:
-      m[0] = v->missing[0].f;
-      m[1] = v->missing[1].f;
-      nm = -2;
-      break;
-    case MISSING_LOW:
-      m[0] = second_lowest_flt64;
-      m[1] = v->missing[0].f;
-      nm = -2;
-      break;
-    case MISSING_HIGH:
-      m[0] = v->missing[0].f;
-      m[1] = FLT64_MAX;
-      nm = -2;
-      break;
-    case MISSING_RANGE_1:
-      m[0] = v->missing[0].f;
-      m[1] = v->missing[1].f;
-      m[2] = v->missing[2].f;
-      nm = -3;
-      break;
-    case MISSING_LOW_1:
-      m[0] = second_lowest_flt64;
-      m[1] = v->missing[0].f;
-      m[2] = v->missing[1].f;
-      nm = -3;
-      break;
-    case MISSING_HIGH_1:
-      m[0] = v->missing[0].f;
-      m[1] = second_lowest_flt64;
-      m[2] = v->missing[1].f;
-      nm = -3;
-      break;
-    default:
-      assert (0);
-      abort ();
+      union value value;
+      mv_pop_value (&mv, &value);
+      if (v->type == NUMERIC)
+        m[nm] = value.f;
+      else
+        buf_copy_rpad ((char *) &m[nm], sizeof m[nm], value.s, v->width);
+      nm++;
     }
+  if (mv_has_range (&v->miss))
+    nm = -nm;
 
   sv.n_missing_values = nm;
   write_format_spec (&v->print, &sv.print);
   write_format_spec (&v->write, &sv.write);
-  memcpy (sv.name, v->name, strlen (v->name));
-  memset (&sv.name[strlen (v->name)], ' ', 8 - strlen (v->name));
+  buf_copy_str_rpad (sv.name, sizeof sv.name, v->short_name);
   if (!buf_write (w, &sv, sizeof sv))
     return 0;
 
@@ -421,7 +454,7 @@ write_variable (struct sfm_writer *w, struct variable *v)
         return 0;
     }
 
-  if (nm && !buf_write (w, m, sizeof *m * nm))
+  if (nm && !buf_write (w, m, sizeof *m * abs (nm)))
     return 0;
 
   if (v->type == ALPHA && v->width > (int) sizeof (flt64))
@@ -543,6 +576,96 @@ write_documents (struct sfm_writer *w, const struct dictionary *d)
   return 1;
 }
 
+/* Write the alignment, width and scale values */
+static int
+write_variable_display_parameters (struct sfm_writer *w, 
+                                  const struct dictionary *dict)
+{
+  int i;
+
+  struct
+  {
+    int32 rec_type P;
+    int32 subtype P;
+    int32 elem_size P;
+    int32 n_elem P;
+  } vdp_hdr;
+
+  vdp_hdr.rec_type = 7;
+  vdp_hdr.subtype = 11;
+  vdp_hdr.elem_size = 4;
+  vdp_hdr.n_elem = w->var_cnt * 3;
+
+  if (!buf_write (w, &vdp_hdr, sizeof vdp_hdr))
+    return 0;
+
+  for ( i = 0 ; i < w->var_cnt ; ++i ) 
+    {
+      struct variable *v;
+      struct
+      {
+       int32 measure P;
+       int32 width P;
+       int32 align P;
+      }
+      params;
+
+      v = dict_get_var(dict, i);
+
+      params.measure = v->measure;
+      params.width = v->display_width;
+      params.align = v->alignment;
+      
+      if (!buf_write (w, &params, sizeof(params)))
+       return 0;
+    }
+  
+  return 1;
+}
+
+/* Writes the long variable name table */
+static int
+write_longvar_table (struct sfm_writer *w, const struct dictionary *dict)
+{
+  struct
+    {
+      int32 rec_type P;
+      int32 subtype P;
+      int32 elem_size P;
+      int32 n_elem P;
+    }
+  lv_hdr;
+
+  struct string long_name_map;
+  size_t i;
+
+  ds_init (&long_name_map, 10 * dict_get_var_cnt (dict));
+  for (i = 0; i < dict_get_var_cnt (dict); i++)
+    {
+      struct variable *v = dict_get_var (dict, i);
+      
+      if (i)
+        ds_putc (&long_name_map, '\t');
+      ds_printf (&long_name_map, "%s=%s", v->short_name, v->name);
+    }
+
+  lv_hdr.rec_type = 7;
+  lv_hdr.subtype = 13;
+  lv_hdr.elem_size = 1;
+  lv_hdr.n_elem = ds_length (&long_name_map);
+
+  if (!buf_write (w, &lv_hdr, sizeof lv_hdr)
+      || !buf_write (w, ds_data (&long_name_map), ds_length (&long_name_map)))
+    goto error;
+
+  ds_destroy (&long_name_map);
+  return 1;
+
+ error:
+  ds_destroy (&long_name_map);
+  return 0;
+}
+
 /* Writes record type 7, subtypes 3 and 4. */
 static int
 write_rec_7_34 (struct sfm_writer *w)