1 /* PSPP - computes sample statistics.
2 Copyright (C) 1997-9, 2000 Free Software Foundation, Inc.
3 Written by Ben Pfaff <blp@gnu.org>.
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License as
7 published by the Free Software Foundation; either version 2 of the
8 License, or (at your option) any later version.
10 This program is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
32 #include "file-handle.h"
37 #include "value-labels.h"
41 #include "debug-print.h"
43 /* pfm writer file_handle extension. */
46 FILE *file; /* Actual file. */
48 int lc; /* Number of characters on this line so far. */
50 int nvars; /* Number of variables. */
51 int *vars; /* Variable widths. */
54 static struct fh_ext_class pfm_w_class;
56 static int bufwrite (struct file_handle *h, const void *buf, size_t nbytes);
57 static int write_header (struct file_handle *h);
58 static int write_version_data (struct file_handle *h);
59 static int write_variables (struct file_handle *h, struct dictionary *d);
60 static int write_value_labels (struct file_handle *h, struct dictionary *d);
62 /* Writes the dictionary DICT to portable file HANDLE. Returns
63 nonzero only if successful. */
65 pfm_write_dictionary (struct file_handle *handle, struct dictionary *dict)
67 struct pfm_fhuser_ext *ext;
69 if (handle->class != NULL)
71 msg (ME, _("Cannot write file %s as portable file: already opened "
73 handle_get_name (handle), handle->class->name);
77 msg (VM (1), _("%s: Opening portable-file handle %s for writing."),
78 handle_get_filename (handle), handle_get_name (handle));
80 /* Open the physical disk file. */
81 handle->class = &pfm_w_class;
82 handle->ext = ext = xmalloc (sizeof (struct pfm_fhuser_ext));
83 ext->file = fopen (handle_get_filename (handle), "wb");
85 if (ext->file == NULL)
87 msg (ME, _("An error occurred while opening \"%s\" for writing "
88 "as a portable file: %s."),
89 handle_get_filename (handle), strerror (errno));
98 ext->nvars = dict_get_var_cnt (dict);
99 ext->vars = xmalloc (sizeof *ext->vars * ext->nvars);
100 for (i = 0; i < ext->nvars; i++)
101 ext->vars[i] = dict_get_var (dict, i)->width;
104 /* Write the file header. */
105 if (!write_header (handle))
108 /* Write version data. */
109 if (!write_version_data (handle))
112 /* Write variables. */
113 if (!write_variables (handle, dict))
116 /* Write value labels. */
117 if (!write_value_labels (handle, dict))
120 /* Write beginning of data marker. */
121 if (!bufwrite (handle, "F", 1))
124 msg (VM (2), _("Wrote portable-file header successfully."));
129 msg (VM (1), _("Error writing portable-file header."));
132 handle->class = NULL;
137 /* Write NBYTES starting at BUF to the portable file represented by
138 H. Break lines properly every 80 characters. */
140 bufwrite (struct file_handle *h, const void *buf_, size_t nbytes)
142 const char *buf = buf_;
143 struct pfm_fhuser_ext *ext = h->ext;
145 assert (buf != NULL);
146 while (nbytes + ext->lc >= 80)
148 size_t n = 80 - ext->lc;
150 if (n && 1 != fwrite (buf, n, 1, ext->file))
153 /* PORTME: line ends. */
154 if (1 != fwrite ("\r\n", 2, 1, ext->file))
162 if (nbytes && 1 != fwrite (buf, nbytes, 1, ext->file))
170 msg (ME, _("%s: Writing portable file: %s."),
171 handle_get_filename (h), strerror (errno));
175 /* Write D to the portable file as a floating-point field, and return
178 write_float (struct file_handle *h, double d)
193 if (d == fabs (SYSMIS) || d == HUGE_VAL)
194 return bufwrite (h, "*.", 2);
196 /* Use GNU libgmp2 to convert D into base-30. */
200 mpf_init_set_d (f, d);
201 mantissa = mpf_get_str (NULL, &exponent, 30, 0, f);
204 for (cp = mantissa; *cp; cp++)
208 /* Choose standard or scientific notation. */
209 mantissa_len = (int) strlen (mantissa);
210 cp = buf = local_alloc (mantissa_len + 32);
213 if (mantissa_len == 0)
215 else if (exponent < -4 || exponent > (mp_exp_t) mantissa_len)
217 /* Scientific notation. */
220 cp = stpcpy (cp, &mantissa[1]);
221 cp = spprintf (cp, "%+ld", (long) (exponent - 1));
223 else if (exponent <= 0)
225 /* Standard notation, D <= 1. */
227 memset (cp, '0', -exponent);
229 cp = stpcpy (cp, mantissa);
233 /* Standard notation, D > 1. */
234 memcpy (cp, mantissa, exponent);
237 cp = stpcpy (cp, &mantissa[exponent]);
241 success = bufwrite (h, buf, cp - buf);
247 /* Write N to the portable file as an integer field, and return success. */
249 write_int (struct file_handle *h, int n)
267 /* PORTME: character codes. */
271 *--bp = r - 10 + 'A';
280 return bufwrite (h, bp, &buf[64] - bp);
283 /* Write S to the portable file as a string field. */
285 write_string (struct file_handle *h, const char *s)
287 size_t n = strlen (s);
288 return write_int (h, (int) n) && bufwrite (h, s, n);
291 /* Write file header. */
293 write_header (struct file_handle *h)
299 for (i = 0; i < 5; i++)
300 if (!bufwrite (h, "ASCII SPSS PORT FILE ", 40))
305 /* PORTME: Translation table from SPSS character code to this
306 computer's native character code (which is probably ASCII). */
307 static const unsigned char spss2ascii[256] =
309 "0000000000000000000000000000000000000000000000000000000000000000"
310 "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz ."
311 "<(+|&[]!$*);^-/|,%_>?`:$@'=\"000000~-0000123456789000-()0{}\\00000"
312 "0000000000000000000000000000000000000000000000000000000000000000"
315 if (!bufwrite (h, spss2ascii, 256))
319 if (!bufwrite (h, "SPSSPORT", 8))
325 /* Writes version, date, and identification records. */
327 write_version_data (struct file_handle *h)
329 if (!bufwrite (h, "A", 1))
339 if ((time_t) -1 == time (&t))
341 tm.tm_sec = tm.tm_min = tm.tm_hour = tm.tm_mon = tm.tm_year = 0;
346 tmp = localtime (&t);
348 sprintf (date_str, "%04d%02d%02d",
349 tmp->tm_year + 1900, tmp->tm_mon + 1, tmp->tm_mday);
350 sprintf (time_str, "%02d%02d%02d", tmp->tm_hour, tmp->tm_min, tmp->tm_sec);
351 if (!write_string (h, date_str) || !write_string (h, time_str))
355 /* Product identification. */
356 if (!bufwrite (h, "1", 1) || !write_string (h, version))
359 /* Subproduct identification. */
360 if (!bufwrite (h, "3", 1) || !write_string (h, host_system))
366 /* Write format F to file H, and return success. */
368 write_format (struct file_handle *h, struct fmt_spec *f)
370 return (write_int (h, formats[f->type].spss)
371 && write_int (h, f->w)
372 && write_int (h, f->d));
375 /* Write value V for variable VV to file H, and return success. */
377 write_value (struct file_handle *h, union value *v, struct variable *vv)
379 if (vv->type == NUMERIC)
380 return write_float (h, v->f);
382 return write_int (h, vv->width) && bufwrite (h, v->s, vv->width);
385 /* Write variable records, and return success. */
387 write_variables (struct file_handle *h, struct dictionary *dict)
391 if (!bufwrite (h, "4", 1) || !write_int (h, dict_get_var_cnt (dict))
392 || !write_int (h, 161))
395 for (i = 0; i < dict_get_var_cnt (dict); i++)
397 static const char *miss_types[MISSING_COUNT] =
399 "", "8", "88", "888", "B ", "9", "A", "B 8", "98", "A8",
405 struct variable *v = dict_get_var (dict, i);
407 if (!bufwrite (h, "7", 1) || !write_int (h, v->width)
408 || !write_string (h, v->name)
409 || !write_format (h, &v->print) || !write_format (h, &v->write))
412 for (m = miss_types[v->miss_type], j = 0; j < (int) strlen (m); j++)
413 if ((m[j] != ' ' && !bufwrite (h, &m[j], 1))
414 || !write_value (h, &v->missing[j], v))
417 if (v->label && (!bufwrite (h, "C", 1) || !write_string (h, v->label)))
424 /* Write value labels to disk. FIXME: Inefficient. */
426 write_value_labels (struct file_handle *h, struct dictionary *dict)
430 for (i = 0; i < dict_get_var_cnt (dict); i++)
432 struct val_labs_iterator *j;
433 struct variable *v = dict_get_var (dict, i);
436 if (!val_labs_count (v->val_labs))
439 if (!bufwrite (h, "D", 1)
441 || !write_string (h, v->name)
442 || !write_int (h, val_labs_count (v->val_labs)))
445 for (vl = val_labs_first_sorted (v->val_labs, &j); vl != NULL;
446 vl = val_labs_next (v->val_labs, &j))
447 if (!write_value (h, &vl->value, v)
448 || !write_string (h, vl->label))
458 /* Writes case ELEM to the portable file represented by H. Returns
461 pfm_write_case (struct file_handle *h, const union value *elem)
463 struct pfm_fhuser_ext *ext = h->ext;
467 for (i = 0; i < ext->nvars; i++)
469 const int width = ext->vars[i];
473 if (!write_float (h, elem[i].f))
478 if (!write_int (h, width) || !bufwrite (h, elem[i].c, width))
486 /* Closes a portable file after we're done with it. */
488 pfm_close (struct file_handle *h)
490 struct pfm_fhuser_ext *ext = h->ext;
495 int n = 80 - ext->lc;
499 memset (buf, 'Z', n);
500 bufwrite (h, buf, n);
503 if (EOF == fclose (ext->file))
504 msg (ME, _("%s: Closing portable file: %s."),
505 handle_get_filename (h), strerror (errno));
511 static struct fh_ext_class pfm_w_class =
514 N_("writing as a portable file"),