#include "error.h"
#include <ctype.h>
#include <errno.h>
+#include <fcntl.h>
#include <float.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
+#include <sys/stat.h>
#include <time.h>
+#include <unistd.h>
#include "alloc.h"
#include "case.h"
#include "dictionary.h"
#include "hash.h"
#include "magic.h"
#include "misc.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"
/* Portable file writer. */
size_t var_cnt; /* Number of variables. */
struct pfm_var *vars; /* Variables. */
+
+ int digits; /* Digits of precision. */
};
/* A variable to write to the portable file. */
static void format_trig_double (long double, int base_10_precision, char[]);
static char *format_trig_int (int, bool force_sign, char[]);
-/* Writes the dictionary DICT to portable file HANDLE. Returns
- nonzero only if successful. DICT will not be modified, except
- to assign short names. */
+/* Returns default options for writing a portable file. */
+struct pfm_write_options
+pfm_writer_default_options (void)
+{
+ struct pfm_write_options opts;
+ opts.create_writeable = true;
+ opts.type = PFM_COMM;
+ opts.digits = DBL_DIG;
+ return opts;
+}
+
+/* Writes the dictionary DICT to portable file HANDLE according
+ to the given OPTS. Returns nonzero only if successful. DICT
+ will not be modified, except to assign short names. */
struct pfm_writer *
-pfm_open_writer (struct file_handle *fh, struct dictionary *dict)
+pfm_open_writer (struct file_handle *fh, struct dictionary *dict,
+ struct pfm_write_options opts)
{
struct pfm_writer *w = NULL;
+ mode_t mode;
+ int fd;
size_t i;
+ /* 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, "portable file", "we"))
goto error;
-
- /* Open the physical disk file. */
+
+ /* Initialize data structures. */
w = xmalloc (sizeof *w);
w->fh = fh;
- w->file = fopen (handle_get_filename (fh), "wb");
+ w->file = fdopen (fd, "w");
+ if (w->file == NULL)
+ {
+ close (fd);
+ goto open_error;
+ }
+
w->lc = 0;
w->var_cnt = 0;
w->vars = NULL;
- /* Check that file create succeeded. */
- if (w->file == NULL)
- {
- msg (ME, _("An error occurred while opening \"%s\" for writing "
- "as a portable file: %s."),
- handle_get_filename (fh), strerror (errno));
- err_cond_fail ();
- goto error;
- }
-
w->var_cnt = dict_get_var_cnt (dict);
w->vars = xmalloc (sizeof *w->vars * w->var_cnt);
for (i = 0; i < w->var_cnt; i++)
pv->fv = dv->fv;
}
+ w->digits = opts.digits;
+ if (w->digits < 1)
+ {
+ msg (ME, _("Invalid decimal digits count %d. Treating as %d."),
+ w->digits, DBL_DIG);
+ w->digits = DBL_DIG;
+ }
+
/* Write file header. */
if (!write_header (w)
|| !write_version_data (w)
return w;
-error:
+ error:
pfm_close_writer (w);
return NULL;
+
+ open_error:
+ msg (ME, _("An error occurred while opening \"%s\" for writing "
+ "as a portable file: %s."),
+ handle_get_filename (fh), strerror (errno));
+ err_cond_fail ();
+ goto error;
}
\f
/* Write NBYTES starting at BUF to the portable file represented by
write_float (struct pfm_writer *w, double d)
{
char buffer[64];
- format_trig_double (d, DBL_DIG, buffer);
+ format_trig_double (d, floor (d) == d ? DBL_DIG : w->digits, buffer);
return buf_write (w, buffer, strlen (buffer)) && buf_write (w, "/", 1);
}
for (i = 0; i < dict_get_var_cnt (dict); i++)
{
- static const char *miss_types[MISSING_COUNT] =
- {
- "", "8", "88", "888", "B ", "9", "A", "B 8", "98", "A8",
- };
-
- const char *m;
- int j;
-
struct variable *v = dict_get_var (dict, i);
+ struct missing_values mv;
if (!buf_write (w, "7", 1) || !write_int (w, v->width)
|| !write_string (w, v->short_name)
|| !write_format (w, &v->print) || !write_format (w, &v->write))
return 0;
- for (m = miss_types[v->miss_type], j = 0; j < (int) strlen (m); j++)
- if ((m[j] != ' ' && !buf_write (w, &m[j], 1))
- || !write_value (w, &v->missing[j], v))
- return 0;
+ /* Write missing values. */
+ mv_copy (&mv, &v->miss);
+ while (mv_has_range (&mv))
+ {
+ double x, y;
+ mv_pop_range (&mv, &x, &y);
+ if (x == LOWEST)
+ {
+ if (!buf_write (w, "9", 1) || !write_float (w, y))
+ return 0;
+ }
+ else if (y == HIGHEST)
+ {
+ if (!buf_write (w, "A", 1) || !write_float (w, y))
+ return 0;
+ }
+ else {
+ if (!buf_write (w, "B", 1) || !write_float (w, x)
+ || !write_float (w, y))
+ return 0;
+ }
+ }
+ while (mv_has_value (&mv))
+ {
+ union value value;
+ mv_pop_value (&mv, &value);
+ if (!buf_write (w, "8", 1) || !write_value (w, &value, v))
+ return 0;
+ }
if (v->label && (!buf_write (w, "C", 1) || !write_string (w, v->label)))
return 0;
required base-30 precision as 2/3 of the base-10 precision
(log30(10) = .68). */
assert (base_10_precision > 0);
+ if (base_10_precision > LDBL_DIG)
+ base_10_precision = LDBL_DIG;
base_30_precision = DIV_RND_UP (base_10_precision * 2, 3);
if (trig_cnt > base_30_precision)
{