1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 1997-2000, 2006-2014 Free Software Foundation, Inc.
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>. */
19 #include "data/sys-file-writer.h"
20 #include "data/sys-file-private.h"
30 #include "data/attributes.h"
31 #include "data/case.h"
32 #include "data/casewriter-provider.h"
33 #include "data/casewriter.h"
34 #include "data/dictionary.h"
35 #include "data/file-handle-def.h"
36 #include "data/format.h"
37 #include "data/make-file.h"
38 #include "data/missing-values.h"
39 #include "data/mrset.h"
40 #include "data/settings.h"
41 #include "data/short-names.h"
42 #include "data/value-labels.h"
43 #include "data/variable.h"
44 #include "libpspp/float-format.h"
45 #include "libpspp/i18n.h"
46 #include "libpspp/integer-format.h"
47 #include "libpspp/message.h"
48 #include "libpspp/misc.h"
49 #include "libpspp/str.h"
50 #include "libpspp/string-array.h"
51 #include "libpspp/version.h"
53 #include "gl/xmemdup0.h"
54 #include "gl/minmax.h"
55 #include "gl/unlocked-io.h"
56 #include "gl/xalloc.h"
59 #define _(msgid) gettext (msgid)
60 #define N_(msgid) (msgid)
62 /* Compression bias used by PSPP. Values between (1 -
63 COMPRESSION_BIAS) and (251 - COMPRESSION_BIAS) inclusive can be
65 #define COMPRESSION_BIAS 100
67 /* System file writer. */
70 struct file_handle
*fh
; /* File handle. */
71 struct fh_lock
*lock
; /* Mutual exclusion for file. */
72 FILE *file
; /* File stream. */
73 struct replace_file
*rf
; /* Ticket for replacing output file. */
75 enum any_compression compression
;
76 casenumber case_cnt
; /* Number of cases written so far. */
77 uint8_t space
; /* ' ' in the file's character encoding. */
79 /* Simple compression buffering.
81 Compressed data is output as a series of 8-byte elements, with 1 to 9
82 such elements clustered together. The first element in a cluster is 8
83 1-byte opcodes. Some opcodes call for an additional element in the
84 cluster (hence, if there are eight such opcodes, then the cluster
85 contains a full 9 elements).
87 cbuf[] holds a cluster at a time. */
89 int n_opcodes
; /* Number of opcodes in cbuf[0] so far. */
90 int n_elements
; /* Number of elements in cbuf[] so far. */
92 /* ZLIB compression. */
93 z_stream zstream
; /* ZLIB deflater. */
95 struct zblock
*blocks
;
96 size_t n_blocks
, allocated_blocks
;
99 struct sfm_var
*sfm_vars
; /* Variables. */
100 size_t sfm_var_cnt
; /* Number of variables. */
101 size_t segment_cnt
; /* Number of variables including extra segments
102 for long string variables. */
107 unsigned int uncompressed_size
;
108 unsigned int compressed_size
;
111 static const struct casewriter_class sys_file_casewriter_class
;
113 static void write_header (struct sfm_writer
*, const struct dictionary
*);
114 static void write_variable (struct sfm_writer
*, const struct variable
*);
115 static void write_value_labels (struct sfm_writer
*,
116 const struct dictionary
*);
117 static void write_integer_info_record (struct sfm_writer
*,
118 const struct dictionary
*);
119 static void write_float_info_record (struct sfm_writer
*);
121 static void write_longvar_table (struct sfm_writer
*w
,
122 const struct dictionary
*dict
);
124 static void write_encoding_record (struct sfm_writer
*w
,
125 const struct dictionary
*);
127 static void write_vls_length_table (struct sfm_writer
*w
,
128 const struct dictionary
*dict
);
130 static void write_long_string_value_labels (struct sfm_writer
*,
131 const struct dictionary
*);
132 static void write_long_string_missing_values (struct sfm_writer
*,
133 const struct dictionary
*);
135 static void write_mrsets (struct sfm_writer
*, const struct dictionary
*,
138 static void write_variable_display_parameters (struct sfm_writer
*w
,
139 const struct dictionary
*dict
);
141 static void write_documents (struct sfm_writer
*, const struct dictionary
*);
143 static void write_data_file_attributes (struct sfm_writer
*,
144 const struct dictionary
*);
145 static void write_variable_attributes (struct sfm_writer
*,
146 const struct dictionary
*);
148 static void write_int (struct sfm_writer
*, int32_t);
149 static void write_int64 (struct sfm_writer
*, int64_t);
150 static inline void convert_double_to_output_format (double, uint8_t[8]);
151 static void write_float (struct sfm_writer
*, double);
152 static void write_string (struct sfm_writer
*, const char *, size_t);
153 static void write_utf8_string (struct sfm_writer
*, const char *encoding
,
154 const char *string
, size_t width
);
155 static void write_utf8_record (struct sfm_writer
*, const char *encoding
,
156 const struct string
*content
, int subtype
);
157 static void write_string_record (struct sfm_writer
*,
158 const struct substring content
, int subtype
);
159 static void write_bytes (struct sfm_writer
*, const void *, size_t);
160 static void write_zeros (struct sfm_writer
*, size_t);
161 static void write_spaces (struct sfm_writer
*, size_t);
162 static void write_value (struct sfm_writer
*, const union value
*, int width
);
164 static void write_case_uncompressed (struct sfm_writer
*,
165 const struct ccase
*);
166 static void write_case_compressed (struct sfm_writer
*, const struct ccase
*);
167 static void flush_compressed (struct sfm_writer
*);
168 static void put_cmp_opcode (struct sfm_writer
*, uint8_t);
169 static void put_cmp_number (struct sfm_writer
*, double);
170 static void put_cmp_string (struct sfm_writer
*, const void *, size_t);
172 static bool start_zstream (struct sfm_writer
*);
173 static void finish_zstream (struct sfm_writer
*);
174 static void write_ztrailer (struct sfm_writer
*);
176 static bool write_error (const struct sfm_writer
*);
177 static bool close_writer (struct sfm_writer
*);
179 /* Returns default options for writing a system file. */
180 struct sfm_write_options
181 sfm_writer_default_options (void)
183 struct sfm_write_options opts
;
184 opts
.compression
= (settings_get_scompression ()
187 opts
.create_writeable
= true;
192 /* Opens the system file designated by file handle FH for writing
193 cases from dictionary D according to the given OPTS.
195 No reference to D is retained, so it may be modified or
196 destroyed at will after this function returns. D is not
197 modified by this function, except to assign short names. */
199 sfm_open_writer (struct file_handle
*fh
, struct dictionary
*d
,
200 struct sfm_write_options opts
)
202 struct encoding_info encoding_info
;
203 struct sfm_writer
*w
;
208 if (opts
.version
!= 2 && opts
.version
!= 3)
210 msg (ME
, _("Unknown system file version %d. Treating as version %d."),
215 /* Create and initialize writer. */
216 w
= xzalloc (sizeof *w
);
222 /* Use the requested compression, except that no EBCDIC-based ZLIB compressed
223 files have been observed, so drop back to simple compression for those
225 w
->compression
= opts
.compression
;
226 if (w
->compression
== ANY_COMP_ZLIB
227 && is_encoding_ebcdic_compatible (dict_get_encoding (d
)))
228 w
->compression
= ANY_COMP_SIMPLE
;
232 w
->n_opcodes
= w
->n_elements
= 0;
233 memset (w
->cbuf
[0], 0, 8);
235 /* Figure out how to map in-memory case data to on-disk case
236 data. Also count the number of segments. Very long strings
237 occupy multiple segments, otherwise each variable only takes
239 w
->segment_cnt
= sfm_dictionary_to_sfm_vars (d
, &w
->sfm_vars
,
242 /* Open file handle as an exclusive writer. */
243 /* TRANSLATORS: this fragment will be interpolated into
244 messages in fh_lock() that identify types of files. */
245 w
->lock
= fh_lock (fh
, FH_REF_FILE
, N_("system file"), FH_ACC_WRITE
, true);
249 /* Create the file on disk. */
251 if (opts
.create_writeable
)
253 w
->rf
= replace_file_start (fh
, "wb", mode
, &w
->file
);
256 msg (ME
, _("Error opening `%s' for writing as a system file: %s."),
257 fh_get_file_name (fh
), strerror (errno
));
261 get_encoding_info (&encoding_info
, dict_get_encoding (d
));
262 w
->space
= encoding_info
.space
[0];
264 /* Write the file header. */
267 /* Write basic variable info. */
268 short_names_assign (d
);
269 for (i
= 0; i
< dict_get_var_cnt (d
); i
++)
270 write_variable (w
, dict_get_var (d
, i
));
272 write_value_labels (w
, d
);
274 if (dict_get_document_line_cnt (d
) > 0)
275 write_documents (w
, d
);
277 write_integer_info_record (w
, d
);
278 write_float_info_record (w
);
280 write_mrsets (w
, d
, true);
282 write_variable_display_parameters (w
, d
);
284 if (opts
.version
>= 3)
285 write_longvar_table (w
, d
);
287 write_vls_length_table (w
, d
);
289 write_long_string_value_labels (w
, d
);
290 write_long_string_missing_values (w
, d
);
292 if (opts
.version
>= 3)
294 if (attrset_count (dict_get_attributes (d
)))
295 write_data_file_attributes (w
, d
);
296 write_variable_attributes (w
, d
);
299 write_mrsets (w
, d
, false);
301 write_encoding_record (w
, d
);
303 /* Write end-of-headers record. */
307 if (w
->compression
== ANY_COMP_ZLIB
)
309 w
->zstream
.zalloc
= Z_NULL
;
310 w
->zstream
.zfree
= Z_NULL
;
311 w
->zstream
.opaque
= Z_NULL
;
312 w
->zstart
= ftello (w
->file
);
314 write_int64 (w
, w
->zstart
);
324 return casewriter_create (dict_get_proto (d
), &sys_file_casewriter_class
, w
);
331 /* Returns value of X truncated to two least-significant digits. */
342 /* Calculates the offset of data for TARGET_VAR from the
343 beginning of each case's data for dictionary D. The return
344 value is in "octs" (8-byte units). */
346 calc_oct_idx (const struct dictionary
*d
, struct variable
*target_var
)
352 for (i
= 0; i
< dict_get_var_cnt (d
); i
++)
354 struct variable
*var
= dict_get_var (d
, i
);
355 if (var
== target_var
)
357 oct_idx
+= sfm_width_to_octs (var_get_width (var
));
362 /* Write the sysfile_header header to system file W. */
364 write_header (struct sfm_writer
*w
, const struct dictionary
*d
)
366 const char *dict_encoding
= dict_get_encoding (d
);
368 char creation_date
[10];
369 char creation_time
[9];
370 const char *file_label
;
371 struct variable
*weight
;
375 /* Record-type code. */
376 if (is_encoding_ebcdic_compatible (dict_encoding
))
377 write_string (w
, EBCDIC_MAGIC
, 4);
378 else if (w
->compression
== ANY_COMP_ZLIB
)
379 write_string (w
, ASCII_ZMAGIC
, 4);
381 write_string (w
, ASCII_MAGIC
, 4);
383 /* Product identification. */
384 snprintf (prod_name
, sizeof prod_name
, "@(#) SPSS DATA FILE %s - %s",
385 version
, host_system
);
386 write_utf8_string (w
, dict_encoding
, prod_name
, 60);
391 /* Number of `union value's per case. */
392 write_int (w
, calc_oct_idx (d
, NULL
));
395 write_int (w
, (w
->compression
== ANY_COMP_NONE
? 0
396 : w
->compression
== ANY_COMP_SIMPLE
? 1
399 /* Weight variable. */
400 weight
= dict_get_weight (d
);
401 write_int (w
, weight
!= NULL
? calc_oct_idx (d
, weight
) + 1 : 0);
403 /* Number of cases. We don't know this in advance, so we write
404 -1 to indicate an unknown number of cases. Later we can
405 come back and overwrite it with the true value. */
408 /* Compression bias. */
409 write_float (w
, COMPRESSION_BIAS
);
411 /* Creation date and time. */
412 if (time (&t
) == (time_t) -1)
414 strcpy (creation_date
, "01 Jan 70");
415 strcpy (creation_time
, "00:00:00");
419 static const char *const month_name
[12] =
421 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
422 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
424 struct tm
*tmp
= localtime (&t
);
425 int day
= rerange (tmp
->tm_mday
);
426 int mon
= rerange (tmp
->tm_mon
+ 1);
427 int year
= rerange (tmp
->tm_year
);
428 int hour
= rerange (tmp
->tm_hour
+ 1);
429 int min
= rerange (tmp
->tm_min
+ 1);
430 int sec
= rerange (tmp
->tm_sec
+ 1);
432 snprintf (creation_date
, sizeof creation_date
,
433 "%02d %s %02d", day
, month_name
[mon
- 1], year
);
434 snprintf (creation_time
, sizeof creation_time
,
435 "%02d:%02d:%02d", hour
- 1, min
- 1, sec
- 1);
437 write_utf8_string (w
, dict_encoding
, creation_date
, 9);
438 write_utf8_string (w
, dict_encoding
, creation_time
, 8);
441 file_label
= dict_get_label (d
);
442 if (file_label
== NULL
)
444 write_utf8_string (w
, dict_encoding
, file_label
, 64);
450 /* Write format spec FMT to W, after adjusting it to be
451 compatible with the given WIDTH. */
453 write_format (struct sfm_writer
*w
, struct fmt_spec fmt
, int width
)
455 assert (fmt_check_output (&fmt
));
456 assert (sfm_width_to_segments (width
) == 1);
459 fmt_resize (&fmt
, width
);
460 write_int (w
, (fmt_to_io (fmt
.type
) << 16) | (fmt
.w
<< 8) | fmt
.d
);
463 /* Write a string continuation variable record for each 8-byte
464 section beyond the initial 8 bytes, for a variable of the
467 write_variable_continuation_records (struct sfm_writer
*w
, int width
)
471 assert (sfm_width_to_segments (width
) == 1);
472 for (position
= 8; position
< width
; position
+= 8)
474 write_int (w
, 2); /* Record type. */
475 write_int (w
, -1); /* Width. */
476 write_int (w
, 0); /* No variable label. */
477 write_int (w
, 0); /* No missing values. */
478 write_int (w
, 0); /* Print format. */
479 write_int (w
, 0); /* Write format. */
480 write_zeros (w
, 8); /* Name. */
484 /* Write the variable record(s) for variable V to system file
487 write_variable (struct sfm_writer
*w
, const struct variable
*v
)
489 int width
= var_get_width (v
);
490 int segment_cnt
= sfm_width_to_segments (width
);
491 int seg0_width
= sfm_segment_alloc_width (width
, 0);
492 const char *encoding
= var_get_encoding (v
);
499 write_int (w
, seg0_width
);
501 /* Variable has a variable label? */
502 write_int (w
, var_has_label (v
));
504 /* Number of missing values. If there is a range, then the
505 range counts as 2 missing values and causes the number to be
508 Missing values for long string variables are written in a separate
510 if (width
<= MAX_SHORT_STRING
)
512 const struct missing_values
*mv
= var_get_missing_values (v
);
513 if (mv_has_range (mv
))
514 write_int (w
, -2 - mv_n_values (mv
));
516 write_int (w
, mv_n_values (mv
));
521 /* Print and write formats. */
522 write_format (w
, *var_get_print_format (v
), seg0_width
);
523 write_format (w
, *var_get_write_format (v
), seg0_width
);
526 The full name is in a translation table written
528 write_utf8_string (w
, encoding
, var_get_short_name (v
, 0), 8);
531 if (var_has_label (v
))
533 char *label
= recode_string (encoding
, UTF8
, var_get_label (v
), -1);
534 size_t label_len
= MIN (strlen (label
), 255);
535 size_t padded_len
= ROUND_UP (label_len
, 4);
536 write_int (w
, label_len
);
537 write_string (w
, label
, padded_len
);
541 /* Write the missing values, if any, range first. */
542 if (width
<= MAX_SHORT_STRING
)
544 const struct missing_values
*mv
= var_get_missing_values (v
);
545 if (mv_has_range (mv
))
548 mv_get_range (mv
, &x
, &y
);
552 for (i
= 0; i
< mv_n_values (mv
); i
++)
553 write_value (w
, mv_get_value (mv
, i
), width
);
556 write_variable_continuation_records (w
, seg0_width
);
558 /* Write additional segments for very long string variables. */
559 for (i
= 1; i
< segment_cnt
; i
++)
561 int seg_width
= sfm_segment_alloc_width (width
, i
);
562 struct fmt_spec fmt
= fmt_for_output (FMT_A
, MAX (seg_width
, 1), 0);
564 write_int (w
, 2); /* Variable record. */
565 write_int (w
, seg_width
); /* Width. */
566 write_int (w
, 0); /* No variable label. */
567 write_int (w
, 0); /* No missing values. */
568 write_format (w
, fmt
, seg_width
); /* Print format. */
569 write_format (w
, fmt
, seg_width
); /* Write format. */
570 write_utf8_string (w
, encoding
, var_get_short_name (v
, i
), 8);
572 write_variable_continuation_records (w
, seg_width
);
576 /* Writes the value labels to system file W.
578 Value labels for long string variables are written separately,
579 by write_long_string_value_labels. */
581 write_value_labels (struct sfm_writer
*w
, const struct dictionary
*d
)
585 struct hmap_node hmap_node
;
586 const struct val_labs
*val_labs
;
588 size_t n_indexes
, allocated_indexes
;
591 size_t n_sets
, allocated_sets
;
592 struct label_set
**sets
;
593 struct hmap same_sets
;
597 n_sets
= allocated_sets
= 0;
599 hmap_init (&same_sets
);
602 for (i
= 0; i
< dict_get_var_cnt (d
); i
++)
604 struct variable
*v
= dict_get_var (d
, i
);
606 if (var_has_value_labels (v
) && var_get_width (v
) <= 8)
608 const struct val_labs
*val_labs
= var_get_value_labels (v
);
609 unsigned int hash
= val_labs_hash (val_labs
, 0);
610 struct label_set
*set
;
612 HMAP_FOR_EACH_WITH_HASH (set
, struct label_set
, hmap_node
,
615 if (val_labs_equal (set
->val_labs
, val_labs
))
617 if (set
->n_indexes
>= set
->allocated_indexes
)
618 set
->indexes
= x2nrealloc (set
->indexes
,
619 &set
->allocated_indexes
,
620 sizeof *set
->indexes
);
621 set
->indexes
[set
->n_indexes
++] = idx
;
626 set
= xmalloc (sizeof *set
);
627 set
->val_labs
= val_labs
;
628 set
->indexes
= xmalloc (sizeof *set
->indexes
);
629 set
->indexes
[0] = idx
;
631 set
->allocated_indexes
= 1;
632 hmap_insert (&same_sets
, &set
->hmap_node
, hash
);
634 if (n_sets
>= allocated_sets
)
635 sets
= x2nrealloc (sets
, &allocated_sets
, sizeof *sets
);
636 sets
[n_sets
++] = set
;
640 idx
+= sfm_width_to_octs (var_get_width (v
));
643 for (i
= 0; i
< n_sets
; i
++)
645 const struct label_set
*set
= sets
[i
];
646 const struct val_labs
*val_labs
= set
->val_labs
;
647 size_t n_labels
= val_labs_count (val_labs
);
648 int width
= val_labs_get_width (val_labs
);
649 const struct val_lab
**labels
;
652 /* Value label record. */
653 write_int (w
, 3); /* Record type. */
654 write_int (w
, n_labels
);
655 labels
= val_labs_sorted (val_labs
);
656 for (j
= 0; j
< n_labels
; j
++)
658 const struct val_lab
*vl
= labels
[j
];
659 char *label
= recode_string (dict_get_encoding (d
), UTF8
,
660 val_lab_get_escaped_label (vl
), -1);
661 uint8_t len
= MIN (strlen (label
), 255);
663 write_value (w
, val_lab_get_value (vl
), width
);
664 write_bytes (w
, &len
, 1);
665 write_bytes (w
, label
, len
);
666 write_zeros (w
, REM_RND_UP (len
+ 1, 8));
671 /* Value label variable record. */
672 write_int (w
, 4); /* Record type. */
673 write_int (w
, set
->n_indexes
);
674 for (j
= 0; j
< set
->n_indexes
; j
++)
675 write_int (w
, set
->indexes
[j
] + 1);
678 for (i
= 0; i
< n_sets
; i
++)
680 struct label_set
*set
= sets
[i
];
686 hmap_destroy (&same_sets
);
689 /* Writes record type 6, document record. */
691 write_documents (struct sfm_writer
*w
, const struct dictionary
*d
)
693 const struct string_array
*docs
= dict_get_documents (d
);
694 const char *enc
= dict_get_encoding (d
);
697 write_int (w
, 6); /* Record type. */
698 write_int (w
, docs
->n
);
699 for (i
= 0; i
< docs
->n
; i
++)
701 char *s
= recode_string (enc
, "UTF-8", docs
->strings
[i
], -1);
702 size_t s_len
= strlen (s
);
703 size_t write_len
= MIN (s_len
, DOC_LINE_LENGTH
);
705 write_bytes (w
, s
, write_len
);
706 write_spaces (w
, DOC_LINE_LENGTH
- write_len
);
712 put_attrset (struct string
*string
, const struct attrset
*attrs
)
714 const struct attribute
*attr
;
715 struct attrset_iterator i
;
717 for (attr
= attrset_first (attrs
, &i
); attr
!= NULL
;
718 attr
= attrset_next (attrs
, &i
))
720 size_t n_values
= attribute_get_n_values (attr
);
723 ds_put_cstr (string
, attribute_get_name (attr
));
724 ds_put_byte (string
, '(');
725 for (j
= 0; j
< n_values
; j
++)
726 ds_put_format (string
, "'%s'\n", attribute_get_value (attr
, j
));
727 ds_put_byte (string
, ')');
732 write_data_file_attributes (struct sfm_writer
*w
,
733 const struct dictionary
*d
)
735 struct string s
= DS_EMPTY_INITIALIZER
;
736 put_attrset (&s
, dict_get_attributes (d
));
737 write_utf8_record (w
, dict_get_encoding (d
), &s
, 17);
742 add_role_attribute (enum var_role role
, struct attrset
*attrs
)
744 struct attribute
*attr
;
774 attrset_delete (attrs
, "$@Role");
776 attr
= attribute_create ("$@Role");
777 attribute_add_value (attr
, s
);
778 attrset_add (attrs
, attr
);
782 write_variable_attributes (struct sfm_writer
*w
, const struct dictionary
*d
)
784 struct string s
= DS_EMPTY_INITIALIZER
;
785 size_t n_vars
= dict_get_var_cnt (d
);
786 size_t n_attrsets
= 0;
789 for (i
= 0; i
< n_vars
; i
++)
791 struct variable
*v
= dict_get_var (d
, i
);
792 struct attrset attrs
;
794 attrset_clone (&attrs
, var_get_attributes (v
));
796 add_role_attribute (var_get_role (v
), &attrs
);
798 ds_put_byte (&s
, '/');
799 ds_put_format (&s
, "%s:", var_get_name (v
));
800 put_attrset (&s
, &attrs
);
801 attrset_destroy (&attrs
);
804 write_utf8_record (w
, dict_get_encoding (d
), &s
, 18);
808 /* Write multiple response sets. If PRE_V14 is true, writes sets supported by
809 SPSS before release 14, otherwise writes sets supported only by later
812 write_mrsets (struct sfm_writer
*w
, const struct dictionary
*dict
,
815 const char *encoding
= dict_get_encoding (dict
);
816 struct string s
= DS_EMPTY_INITIALIZER
;
820 if (is_encoding_ebcdic_compatible (encoding
))
826 n_mrsets
= dict_get_n_mrsets (dict
);
830 for (i
= 0; i
< n_mrsets
; i
++)
832 const struct mrset
*mrset
= dict_get_mrset (dict
, i
);
836 if ((mrset
->type
!= MRSET_MD
|| mrset
->cat_source
!= MRSET_COUNTEDVALUES
)
840 name
= recode_string (encoding
, "UTF-8", mrset
->name
, -1);
841 ds_put_format (&s
, "%s=", name
);
844 if (mrset
->type
== MRSET_MD
)
848 if (mrset
->cat_source
== MRSET_COUNTEDVALUES
)
849 ds_put_format (&s
, "E %d ", mrset
->label_from_var_label
? 11 : 1);
851 ds_put_byte (&s
, 'D');
853 if (mrset
->width
== 0)
854 counted
= xasprintf ("%.0f", mrset
->counted
.f
);
856 counted
= xmemdup0 (value_str (&mrset
->counted
, mrset
->width
),
858 ds_put_format (&s
, "%zu %s", strlen (counted
), counted
);
862 ds_put_byte (&s
, 'C');
863 ds_put_byte (&s
, ' ');
865 if (mrset
->label
&& !mrset
->label_from_var_label
)
867 char *label
= recode_string (encoding
, "UTF-8", mrset
->label
, -1);
868 ds_put_format (&s
, "%zu %s", strlen (label
), label
);
872 ds_put_cstr (&s
, "0 ");
874 for (j
= 0; j
< mrset
->n_vars
; j
++)
876 const char *short_name_utf8
= var_get_short_name (mrset
->vars
[j
], 0);
877 char *lower_name_utf8
= utf8_to_lower (short_name_utf8
);
878 char *short_name
= recode_string (encoding
, "UTF-8",
879 lower_name_utf8
, -1);
880 ds_put_format (&s
, " %s", short_name
);
882 free (lower_name_utf8
);
884 ds_put_byte (&s
, '\n');
887 if (!ds_is_empty (&s
))
888 write_string_record (w
, ds_ss (&s
), pre_v14
? 7 : 19);
892 /* Write the alignment, width and scale values. */
894 write_variable_display_parameters (struct sfm_writer
*w
,
895 const struct dictionary
*dict
)
899 write_int (w
, 7); /* Record type. */
900 write_int (w
, 11); /* Record subtype. */
901 write_int (w
, 4); /* Data item (int32) size. */
902 write_int (w
, w
->segment_cnt
* 3); /* Number of data items. */
904 for (i
= 0; i
< dict_get_var_cnt (dict
); ++i
)
906 struct variable
*v
= dict_get_var (dict
, i
);
907 int width
= var_get_width (v
);
908 int segment_cnt
= sfm_width_to_segments (width
);
909 int measure
= (var_get_measure (v
) == MEASURE_NOMINAL
? 1
910 : var_get_measure (v
) == MEASURE_ORDINAL
? 2
912 int alignment
= (var_get_alignment (v
) == ALIGN_LEFT
? 0
913 : var_get_alignment (v
) == ALIGN_RIGHT
? 1
917 for (i
= 0; i
< segment_cnt
; i
++)
919 int width_left
= width
- sfm_segment_effective_offset (width
, i
);
920 write_int (w
, measure
);
921 write_int (w
, (i
== 0 ? var_get_display_width (v
)
922 : var_default_display_width (width_left
)));
923 write_int (w
, alignment
);
928 /* Writes the table of lengths for very long string variables. */
930 write_vls_length_table (struct sfm_writer
*w
,
931 const struct dictionary
*dict
)
936 ds_init_empty (&map
);
937 for (i
= 0; i
< dict_get_var_cnt (dict
); ++i
)
939 const struct variable
*v
= dict_get_var (dict
, i
);
940 if (sfm_width_to_segments (var_get_width (v
)) > 1)
941 ds_put_format (&map
, "%s=%05d%c\t",
942 var_get_short_name (v
, 0), var_get_width (v
), 0);
944 if (!ds_is_empty (&map
))
945 write_utf8_record (w
, dict_get_encoding (dict
), &map
, 14);
950 write_long_string_value_labels (struct sfm_writer
*w
,
951 const struct dictionary
*dict
)
953 const char *encoding
= dict_get_encoding (dict
);
954 size_t n_vars
= dict_get_var_cnt (dict
);
958 /* Figure out the size in advance. */
960 for (i
= 0; i
< n_vars
; i
++)
962 struct variable
*var
= dict_get_var (dict
, i
);
963 const struct val_labs
*val_labs
= var_get_value_labels (var
);
964 int width
= var_get_width (var
);
965 const struct val_lab
*val_lab
;
967 if (val_labs_count (val_labs
) == 0 || width
< 9)
971 size
+= recode_string_len (encoding
, "UTF-8", var_get_name (var
), -1);
972 for (val_lab
= val_labs_first (val_labs
); val_lab
!= NULL
;
973 val_lab
= val_labs_next (val_labs
, val_lab
))
976 size
+= recode_string_len (encoding
, "UTF-8",
977 val_lab_get_escaped_label (val_lab
), -1);
983 write_int (w
, 7); /* Record type. */
984 write_int (w
, 21); /* Record subtype */
985 write_int (w
, 1); /* Data item (byte) size. */
986 write_int (w
, size
); /* Number of data items. */
988 start
= ftello (w
->file
);
989 for (i
= 0; i
< n_vars
; i
++)
991 struct variable
*var
= dict_get_var (dict
, i
);
992 const struct val_labs
*val_labs
= var_get_value_labels (var
);
993 int width
= var_get_width (var
);
994 const struct val_lab
*val_lab
;
997 if (val_labs_count (val_labs
) == 0 || width
< 9)
1000 var_name
= recode_string (encoding
, "UTF-8", var_get_name (var
), -1);
1001 write_int (w
, strlen (var_name
));
1002 write_bytes (w
, var_name
, strlen (var_name
));
1005 write_int (w
, width
);
1006 write_int (w
, val_labs_count (val_labs
));
1007 for (val_lab
= val_labs_first (val_labs
); val_lab
!= NULL
;
1008 val_lab
= val_labs_next (val_labs
, val_lab
))
1013 write_int (w
, width
);
1014 write_bytes (w
, value_str (val_lab_get_value (val_lab
), width
),
1017 label
= recode_string (var_get_encoding (var
), "UTF-8",
1018 val_lab_get_escaped_label (val_lab
), -1);
1019 len
= strlen (label
);
1021 write_bytes (w
, label
, len
);
1025 assert (ftello (w
->file
) == start
+ size
);
1029 write_long_string_missing_values (struct sfm_writer
*w
,
1030 const struct dictionary
*dict
)
1032 const char *encoding
= dict_get_encoding (dict
);
1033 size_t n_vars
= dict_get_var_cnt (dict
);
1037 /* Figure out the size in advance. */
1039 for (i
= 0; i
< n_vars
; i
++)
1041 struct variable
*var
= dict_get_var (dict
, i
);
1042 const struct missing_values
*mv
= var_get_missing_values (var
);
1043 int width
= var_get_width (var
);
1045 if (mv_is_empty (mv
) || width
< 9)
1049 size
+= recode_string_len (encoding
, "UTF-8", var_get_name (var
), -1);
1051 size
+= mv_n_values (mv
) * (4 + 8);
1056 write_int (w
, 7); /* Record type. */
1057 write_int (w
, 22); /* Record subtype */
1058 write_int (w
, 1); /* Data item (byte) size. */
1059 write_int (w
, size
); /* Number of data items. */
1061 start
= ftello (w
->file
);
1062 for (i
= 0; i
< n_vars
; i
++)
1064 struct variable
*var
= dict_get_var (dict
, i
);
1065 const struct missing_values
*mv
= var_get_missing_values (var
);
1066 int width
= var_get_width (var
);
1067 uint8_t n_missing_values
;
1071 if (mv_is_empty (mv
) || width
< 9)
1074 var_name
= recode_string (encoding
, "UTF-8", var_get_name (var
), -1);
1075 write_int (w
, strlen (var_name
));
1076 write_bytes (w
, var_name
, strlen (var_name
));
1079 n_missing_values
= mv_n_values (mv
);
1080 write_bytes (w
, &n_missing_values
, 1);
1082 for (j
= 0; j
< n_missing_values
; j
++)
1084 const union value
*value
= mv_get_value (mv
, j
);
1087 write_bytes (w
, value_str (value
, width
), 8);
1090 assert (ftello (w
->file
) == start
+ size
);
1094 write_encoding_record (struct sfm_writer
*w
,
1095 const struct dictionary
*d
)
1097 /* IANA says "...character set names may be up to 40 characters taken
1098 from the printable characters of US-ASCII," so character set names
1099 don't need to be recoded to be in UTF-8.
1101 We convert encoding names to uppercase because SPSS writes encoding
1102 names in uppercase. */
1103 char *encoding
= xstrdup (dict_get_encoding (d
));
1104 str_uppercase (encoding
);
1105 write_string_record (w
, ss_cstr (encoding
), 20);
1109 /* Writes the long variable name table. */
1111 write_longvar_table (struct sfm_writer
*w
, const struct dictionary
*dict
)
1116 ds_init_empty (&map
);
1117 for (i
= 0; i
< dict_get_var_cnt (dict
); i
++)
1119 struct variable
*v
= dict_get_var (dict
, i
);
1121 ds_put_byte (&map
, '\t');
1122 ds_put_format (&map
, "%s=%s",
1123 var_get_short_name (v
, 0), var_get_name (v
));
1125 write_utf8_record (w
, dict_get_encoding (dict
), &map
, 13);
1129 /* Write integer information record. */
1131 write_integer_info_record (struct sfm_writer
*w
,
1132 const struct dictionary
*d
)
1134 const char *dict_encoding
= dict_get_encoding (d
);
1135 int version_component
[3];
1139 /* Parse the version string. */
1140 memset (version_component
, 0, sizeof version_component
);
1141 sscanf (bare_version
, "%d.%d.%d",
1142 &version_component
[0], &version_component
[1], &version_component
[2]);
1144 /* Figure out the floating-point format. */
1145 if (FLOAT_NATIVE_64_BIT
== FLOAT_IEEE_DOUBLE_LE
1146 || FLOAT_NATIVE_64_BIT
== FLOAT_IEEE_DOUBLE_BE
)
1148 else if (FLOAT_NATIVE_64_BIT
== FLOAT_Z_LONG
)
1150 else if (FLOAT_NATIVE_64_BIT
== FLOAT_VAX_D
)
1155 /* Choose codepage. */
1156 codepage
= sys_get_codepage_from_encoding (dict_encoding
);
1159 /* The codepage is unknown. Choose a default.
1161 For an EBCDIC-compatible encoding, use the value for EBCDIC.
1163 For an ASCII-compatible encoding, default to "7-bit ASCII", because
1164 many files use this codepage number regardless of their actual
1167 if (is_encoding_ascii_compatible (dict_encoding
))
1169 else if (is_encoding_ebcdic_compatible (dict_encoding
))
1174 write_int (w
, 7); /* Record type. */
1175 write_int (w
, 3); /* Record subtype. */
1176 write_int (w
, 4); /* Data item (int32) size. */
1177 write_int (w
, 8); /* Number of data items. */
1178 write_int (w
, version_component
[0]);
1179 write_int (w
, version_component
[1]);
1180 write_int (w
, version_component
[2]);
1181 write_int (w
, -1); /* Machine code. */
1182 write_int (w
, float_format
);
1183 write_int (w
, 1); /* Compression code. */
1184 write_int (w
, INTEGER_NATIVE
== INTEGER_MSB_FIRST
? 1 : 2);
1185 write_int (w
, codepage
);
1188 /* Write floating-point information record. */
1190 write_float_info_record (struct sfm_writer
*w
)
1192 write_int (w
, 7); /* Record type. */
1193 write_int (w
, 4); /* Record subtype. */
1194 write_int (w
, 8); /* Data item (flt64) size. */
1195 write_int (w
, 3); /* Number of data items. */
1196 write_float (w
, SYSMIS
); /* System-missing value. */
1197 write_float (w
, HIGHEST
); /* Value used for HIGHEST in missing values. */
1198 write_float (w
, LOWEST
); /* Value used for LOWEST in missing values. */
1201 /* Writes case C to system file W. */
1203 sys_file_casewriter_write (struct casewriter
*writer
, void *w_
,
1206 struct sfm_writer
*w
= w_
;
1208 if (ferror (w
->file
))
1210 casewriter_force_error (writer
);
1217 if (w
->compression
== ANY_COMP_NONE
)
1218 write_case_uncompressed (w
, c
);
1220 write_case_compressed (w
, c
);
1225 /* Destroys system file writer W. */
1227 sys_file_casewriter_destroy (struct casewriter
*writer
, void *w_
)
1229 struct sfm_writer
*w
= w_
;
1230 if (!close_writer (w
))
1231 casewriter_force_error (writer
);
1234 /* Returns true if an I/O error has occurred on WRITER, false otherwise. */
1236 write_error (const struct sfm_writer
*writer
)
1238 return ferror (writer
->file
);
1241 /* Closes a system file after we're done with it.
1242 Returns true if successful, false if an I/O error occurred. */
1244 close_writer (struct sfm_writer
*w
)
1252 if (w
->file
!= NULL
)
1255 flush_compressed (w
);
1256 if (w
->compression
== ANY_COMP_ZLIB
)
1263 ok
= !write_error (w
);
1265 /* Seek back to the beginning and update the number of cases.
1266 This is just a courtesy to later readers, so there's no need
1267 to check return values or report errors. */
1268 if (ok
&& w
->case_cnt
<= INT32_MAX
&& !fseeko (w
->file
, 80, SEEK_SET
))
1270 write_int (w
, w
->case_cnt
);
1274 if (fclose (w
->file
) == EOF
)
1278 msg (ME
, _("An I/O error occurred writing system file `%s'."),
1279 fh_get_file_name (w
->fh
));
1281 if (ok
? !replace_file_commit (w
->rf
) : !replace_file_abort (w
->rf
))
1287 fh_unlock (w
->lock
);
1296 /* System file writer casewriter class. */
1297 static const struct casewriter_class sys_file_casewriter_class
=
1299 sys_file_casewriter_write
,
1300 sys_file_casewriter_destroy
,
1304 /* Writes case C to system file W, without compressing it. */
1306 write_case_uncompressed (struct sfm_writer
*w
, const struct ccase
*c
)
1310 for (i
= 0; i
< w
->sfm_var_cnt
; i
++)
1312 struct sfm_var
*v
= &w
->sfm_vars
[i
];
1314 if (v
->var_width
== 0)
1315 write_float (w
, case_num_idx (c
, v
->case_index
));
1318 write_bytes (w
, case_str_idx (c
, v
->case_index
) + v
->offset
,
1320 write_spaces (w
, v
->padding
);
1325 /* Writes case C to system file W, with compression. */
1327 write_case_compressed (struct sfm_writer
*w
, const struct ccase
*c
)
1331 for (i
= 0; i
< w
->sfm_var_cnt
; i
++)
1333 struct sfm_var
*v
= &w
->sfm_vars
[i
];
1335 if (v
->var_width
== 0)
1337 double d
= case_num_idx (c
, v
->case_index
);
1339 put_cmp_opcode (w
, 255);
1340 else if (d
>= 1 - COMPRESSION_BIAS
1341 && d
<= 251 - COMPRESSION_BIAS
1343 put_cmp_opcode (w
, (int) d
+ COMPRESSION_BIAS
);
1345 put_cmp_number (w
, d
);
1349 int offset
= v
->offset
;
1352 /* This code properly deals with a width that is not a
1353 multiple of 8, by ensuring that the final partial
1354 oct (8 byte unit) is treated as padded with spaces
1356 for (width
= v
->segment_width
; width
> 0; width
-= 8, offset
+= 8)
1358 const void *data
= case_str_idx (c
, v
->case_index
) + offset
;
1359 int chunk_size
= MIN (width
, 8);
1360 if (!memcmp (data
, " ", chunk_size
))
1361 put_cmp_opcode (w
, 254);
1363 put_cmp_string (w
, data
, chunk_size
);
1366 /* This code deals properly with padding that is not a
1367 multiple of 8 bytes, by discarding the remainder,
1368 which was already effectively padded with spaces in
1369 the previous loop. (Note that v->width + v->padding
1370 is always a multiple of 8.) */
1371 for (padding
= v
->padding
/ 8; padding
> 0; padding
--)
1372 put_cmp_opcode (w
, 254);
1378 start_zstream (struct sfm_writer
*w
)
1382 error
= deflateInit (&w
->zstream
, 1);
1385 msg (ME
, _("Failed to initialize ZLIB for compression (%s)."),
1393 finish_zstream (struct sfm_writer
*w
)
1395 struct zblock
*block
;
1398 assert (w
->zstream
.total_in
<= ZBLOCK_SIZE
);
1400 w
->zstream
.next_in
= NULL
;
1401 w
->zstream
.avail_in
= 0;
1406 w
->zstream
.next_out
= buf
;
1407 w
->zstream
.avail_out
= sizeof buf
;
1408 error
= deflate (&w
->zstream
, Z_FINISH
);
1409 write_bytes (w
, buf
, w
->zstream
.next_out
- buf
);
1411 while (error
== Z_OK
);
1413 if (error
!= Z_STREAM_END
)
1414 msg (ME
, _("Failed to complete ZLIB stream compression (%s)."),
1417 if (w
->n_blocks
>= w
->allocated_blocks
)
1418 w
->blocks
= x2nrealloc (w
->blocks
, &w
->allocated_blocks
,
1420 block
= &w
->blocks
[w
->n_blocks
++];
1421 block
->uncompressed_size
= w
->zstream
.total_in
;
1422 block
->compressed_size
= w
->zstream
.total_out
;
1423 deflateEnd (&w
->zstream
);
1427 write_zlib (struct sfm_writer
*w
, const void *data_
, unsigned int n
)
1429 const uint8_t *data
= data_
;
1435 if (w
->zstream
.total_in
>= ZBLOCK_SIZE
)
1441 chunk
= MIN (n
, ZBLOCK_SIZE
- w
->zstream
.total_in
);
1443 w
->zstream
.next_in
= CONST_CAST (uint8_t *, data
);
1444 w
->zstream
.avail_in
= chunk
;
1450 w
->zstream
.next_out
= buf
;
1451 w
->zstream
.avail_out
= sizeof buf
;
1452 error
= deflate (&w
->zstream
, Z_NO_FLUSH
);
1453 write_bytes (w
, buf
, w
->zstream
.next_out
- buf
);
1456 msg (ME
, _("ZLIB stream compression failed (%s)."),
1461 while (w
->zstream
.avail_in
> 0 || w
->zstream
.avail_out
== 0);
1468 write_ztrailer (struct sfm_writer
*w
)
1470 long long int uncompressed_ofs
;
1471 long long int compressed_ofs
;
1472 const struct zblock
*block
;
1474 write_int64 (w
, -COMPRESSION_BIAS
);
1476 write_int (w
, ZBLOCK_SIZE
);
1477 write_int (w
, w
->n_blocks
);
1479 uncompressed_ofs
= w
->zstart
;
1480 compressed_ofs
= w
->zstart
+ 24;
1481 for (block
= w
->blocks
; block
< &w
->blocks
[w
->n_blocks
]; block
++)
1483 write_int64 (w
, uncompressed_ofs
);
1484 write_int64 (w
, compressed_ofs
);
1485 write_int (w
, block
->uncompressed_size
);
1486 write_int (w
, block
->compressed_size
);
1488 uncompressed_ofs
+= block
->uncompressed_size
;
1489 compressed_ofs
+= block
->compressed_size
;
1492 if (!fseeko (w
->file
, w
->zstart
+ 8, SEEK_SET
))
1494 write_int64 (w
, compressed_ofs
);
1495 write_int64 (w
, 24 + (w
->n_blocks
* 24));
1498 msg (ME
, _("%s: Seek failed (%s)."),
1499 fh_get_file_name (w
->fh
), strerror (errno
));
1502 /* Flushes buffered compressed opcodes and data to W. */
1504 flush_compressed (struct sfm_writer
*w
)
1508 unsigned int n
= 8 * (1 + w
->n_elements
);
1509 if (w
->compression
== ANY_COMP_SIMPLE
)
1510 write_bytes (w
, w
->cbuf
, n
);
1512 write_zlib (w
, w
->cbuf
, n
);
1514 w
->n_opcodes
= w
->n_elements
= 0;
1515 memset (w
->cbuf
[0], 0, 8);
1519 /* Appends OPCODE to the buffered set of compression opcodes in
1520 W. Flushes the compression buffer beforehand if necessary. */
1522 put_cmp_opcode (struct sfm_writer
*w
, uint8_t opcode
)
1524 if (w
->n_opcodes
>= 8)
1525 flush_compressed (w
);
1527 w
->cbuf
[0][w
->n_opcodes
++] = opcode
;
1530 /* Appends NUMBER to the buffered compression data in W. */
1532 put_cmp_number (struct sfm_writer
*w
, double number
)
1534 put_cmp_opcode (w
, 253);
1535 convert_double_to_output_format (number
, w
->cbuf
[++w
->n_elements
]);
1538 /* Appends SIZE bytes of DATA to the buffered compression data in
1539 W, followed by enough spaces to pad the output data to exactly
1540 8 bytes (thus, SIZE must be no greater than 8). */
1542 put_cmp_string (struct sfm_writer
*w
, const void *data
, size_t size
)
1546 put_cmp_opcode (w
, 253);
1548 memset (w
->cbuf
[w
->n_elements
], w
->space
, 8);
1549 memcpy (w
->cbuf
[w
->n_elements
], data
, size
);
1552 /* Writes 32-bit integer X to the output file for writer W. */
1554 write_int (struct sfm_writer
*w
, int32_t x
)
1556 write_bytes (w
, &x
, sizeof x
);
1559 /* Writes 64-bit integer X to the output file for writer W. */
1561 write_int64 (struct sfm_writer
*w
, int64_t x
)
1563 write_bytes (w
, &x
, sizeof x
);
1566 /* Converts NATIVE to the 64-bit format used in output files in
1569 convert_double_to_output_format (double native
, uint8_t output
[8])
1571 /* If "double" is not a 64-bit type, then convert it to a
1572 64-bit type. Otherwise just copy it. */
1573 if (FLOAT_NATIVE_DOUBLE
!= FLOAT_NATIVE_64_BIT
)
1574 float_convert (FLOAT_NATIVE_DOUBLE
, &native
, FLOAT_NATIVE_64_BIT
, output
);
1576 memcpy (output
, &native
, sizeof native
);
1579 /* Writes floating-point number X to the output file for writer
1582 write_float (struct sfm_writer
*w
, double x
)
1585 convert_double_to_output_format (x
, output
);
1586 write_bytes (w
, output
, sizeof output
);
1589 /* Writes contents of VALUE with the given WIDTH to W, padding
1590 with zeros to a multiple of 8 bytes.
1591 To avoid a branch, and because we don't actually need to
1592 support it, WIDTH must be no bigger than 8. */
1594 write_value (struct sfm_writer
*w
, const union value
*value
, int width
)
1596 assert (width
<= 8);
1598 write_float (w
, value
->f
);
1601 write_bytes (w
, value_str (value
, width
), width
);
1602 write_zeros (w
, 8 - width
);
1606 /* Writes null-terminated STRING in a field of the given WIDTH to W. If STRING
1607 is longer than WIDTH, it is truncated; if STRING is shorter than WIDTH, it
1608 is padded on the right with spaces. */
1610 write_string (struct sfm_writer
*w
, const char *string
, size_t width
)
1612 size_t data_bytes
= MIN (strlen (string
), width
);
1613 size_t pad_bytes
= width
- data_bytes
;
1614 write_bytes (w
, string
, data_bytes
);
1615 while (pad_bytes
-- > 0)
1616 putc (w
->space
, w
->file
);
1619 /* Recodes null-terminated UTF-8 encoded STRING into ENCODING, and writes the
1620 recoded version in a field of the given WIDTH to W. The string is truncated
1621 or padded on the right with spaces to exactly WIDTH bytes. */
1623 write_utf8_string (struct sfm_writer
*w
, const char *encoding
,
1624 const char *string
, size_t width
)
1626 char *s
= recode_string (encoding
, "UTF-8", string
, -1);
1627 write_string (w
, s
, width
);
1631 /* Writes a record with type 7, subtype SUBTYPE that contains CONTENT recoded
1632 from UTF-8 encoded into ENCODING. */
1634 write_utf8_record (struct sfm_writer
*w
, const char *encoding
,
1635 const struct string
*content
, int subtype
)
1639 s
= recode_substring_pool (encoding
, "UTF-8", ds_ss (content
), NULL
);
1640 write_string_record (w
, s
, subtype
);
1644 /* Writes a record with type 7, subtype SUBTYPE that contains the string
1647 write_string_record (struct sfm_writer
*w
,
1648 const struct substring content
, int subtype
)
1651 write_int (w
, subtype
);
1653 write_int (w
, ss_length (content
));
1654 write_bytes (w
, ss_data (content
), ss_length (content
));
1657 /* Writes SIZE bytes of DATA to W's output file. */
1659 write_bytes (struct sfm_writer
*w
, const void *data
, size_t size
)
1661 fwrite (data
, 1, size
, w
->file
);
1664 /* Writes N zeros to W's output file. */
1666 write_zeros (struct sfm_writer
*w
, size_t n
)
1672 /* Writes N spaces to W's output file. */
1674 write_spaces (struct sfm_writer
*w
, size_t n
)
1677 putc (w
->space
, w
->file
);