X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=src%2Fpfm-write.c;h=cd088b7b85048be501fbd2ccbec3c45b316ddcee;hb=4611b8e15a8286d4039ce64b59e5a891af549238;hp=a4b80bd079321d4b669809ec6cbd7e66d8c273f7;hpb=80cf4c16f1bc0ee3bcc1f4ab6abe8e55aa1d1219;p=pspp diff --git a/src/pfm-write.c b/src/pfm-write.c index a4b80bd079..cd088b7b85 100644 --- a/src/pfm-write.c +++ b/src/pfm-write.c @@ -22,11 +22,14 @@ #include "error.h" #include #include +#include #include #include #include #include +#include #include +#include #include "alloc.h" #include "case.h" #include "dictionary.h" @@ -35,11 +38,15 @@ #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. */ @@ -52,6 +59,8 @@ struct pfm_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. */ @@ -70,36 +79,55 @@ static int write_value_labels (struct pfm_writer *, const struct dictionary *); 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++) @@ -110,6 +138,14 @@ pfm_open_writer (struct file_handle *fh, struct dictionary *dict) 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) @@ -120,9 +156,16 @@ pfm_open_writer (struct file_handle *fh, struct dictionary *dict) 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; } /* Write NBYTES starting at BUF to the portable file represented by @@ -166,7 +209,7 @@ static int 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); } @@ -295,25 +338,43 @@ write_variables (struct pfm_writer *w, struct dictionary *dict) 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; @@ -412,7 +473,15 @@ pfm_close_writer (struct pfm_writer *w) free (w); } -/* Base-30 conversion. */ +/* Base-30 conversion. + + Portable files represent numbers in base-30 format, so we need + to be able to convert real and integer number to that base. + Older versions of PSPP used libgmp to do so, but this added a + big library dependency to do just one thing. Now we do it + ourselves internally. + + Important fact: base 30 is called "trigesimal". */ /* Conversion base. */ #define BASE 30 /* As an integer. */ @@ -422,30 +491,36 @@ pfm_close_writer (struct pfm_writer *w) digits that a `long int' can hold. */ #define CHUNK_SIZE 6 -/* Yields the square of X. */ -#define Q(X) ((X) * (X)) +/* pow_tab[i] = pow (30, pow (2, i)) */ +static long double pow_tab[16]; + +/* Initializes pow_tab[]. */ +static void +init_pow_tab (void) +{ + static bool did_init = false; + long double power; + size_t i; + + /* Only initialize once. */ + if (did_init) + return; + did_init = true; + + /* Set each element of pow_tab[] until we run out of numerical + range. */ + i = 0; + for (power = 30.0L; power < DBL_MAX; power *= power) + { + assert (i < sizeof pow_tab / sizeof *pow_tab); + pow_tab[i++] = power; + } +} /* Returns 30**EXPONENT, for 0 <= EXPONENT <= log30(DBL_MAX). */ static long double pow30_nonnegative (int exponent) { - /* pow_tab[i] = pow (30, pow (2, i)) */ - static const long double pow_tab[] = - { - LDBASE, - Q (LDBASE), - Q (Q (LDBASE)), - Q (Q (Q (LDBASE))), - Q (Q (Q (Q (LDBASE)))), - Q (Q (Q (Q (Q (LDBASE))))), - Q (Q (Q (Q (Q (Q (LDBASE)))))), - Q (Q (Q (Q (Q (Q (Q (LDBASE))))))), - Q (Q (Q (Q (Q (Q (Q (Q (LDBASE)))))))), - Q (Q (Q (Q (Q (Q (Q (Q (Q (LDBASE))))))))), - Q (Q (Q (Q (Q (Q (Q (Q (Q (Q (LDBASE)))))))))), - Q (Q (Q (Q (Q (Q (Q (Q (Q (Q (Q (LDBASE))))))))))), - }; - long double power; int i; @@ -638,6 +713,8 @@ format_trig_double (long double value, int base_10_precision, char output[]) /* Number of trigesimal places left to write into BUFFER. */ int trigs_to_output; + init_pow_tab (); + /* Handle special cases. */ if (value == SYSMIS) goto missing_value; @@ -717,6 +794,8 @@ format_trig_double (long double value, int base_10_precision, char output[]) 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) {