sys-file-writer: Simplify use of put_cmp_number(), put_cmp_string().
[pspp] / src / data / sys-file-writer.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 1997-2000, 2006-2013 Free Software Foundation, Inc.
3
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.
8
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.
13
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/>. */
16
17 #include <config.h>
18
19 #include "data/sys-file-writer.h"
20 #include "data/sys-file-private.h"
21
22 #include <ctype.h>
23 #include <errno.h>
24 #include <stdint.h>
25 #include <stdlib.h>
26 #include <sys/stat.h>
27 #include <time.h>
28
29 #include "data/attributes.h"
30 #include "data/case.h"
31 #include "data/casewriter-provider.h"
32 #include "data/casewriter.h"
33 #include "data/dictionary.h"
34 #include "data/file-handle-def.h"
35 #include "data/file-name.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"
52
53 #include "gl/xmemdup0.h"
54 #include "gl/minmax.h"
55 #include "gl/unlocked-io.h"
56 #include "gl/xalloc.h"
57
58 #include "gettext.h"
59 #define _(msgid) gettext (msgid)
60 #define N_(msgid) (msgid)
61
62 /* Compression bias used by PSPP.  Values between (1 -
63    COMPRESSION_BIAS) and (251 - COMPRESSION_BIAS) inclusive can be
64    compressed. */
65 #define COMPRESSION_BIAS 100
66
67 /* System file writer. */
68 struct sfm_writer
69   {
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. */
74
75     bool compress;              /* 1=compressed, 0=not compressed. */
76     casenumber case_cnt;        /* Number of cases written so far. */
77     uint8_t space;              /* ' ' in the file's character encoding. */
78
79     /* Compression buffering.
80
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).
86
87        cbuf[] holds a cluster at a time. */
88     uint8_t cbuf[9][8];
89     int n_opcodes;              /* Number of opcodes in cbuf[0] so far. */
90     int n_elements;             /* Number of elements in cbuf[] so far. */
91
92     /* Variables. */
93     struct sfm_var *sfm_vars;   /* Variables. */
94     size_t sfm_var_cnt;         /* Number of variables. */
95     size_t segment_cnt;         /* Number of variables including extra segments
96                                    for long string variables. */
97   };
98
99 static const struct casewriter_class sys_file_casewriter_class;
100
101 static void write_header (struct sfm_writer *, const struct dictionary *);
102 static void write_variable (struct sfm_writer *, const struct variable *);
103 static void write_value_labels (struct sfm_writer *,
104                                 const struct dictionary *);
105 static void write_integer_info_record (struct sfm_writer *,
106                                        const struct dictionary *);
107 static void write_float_info_record (struct sfm_writer *);
108
109 static void write_longvar_table (struct sfm_writer *w,
110                                  const struct dictionary *dict);
111
112 static void write_encoding_record (struct sfm_writer *w,
113                                    const struct dictionary *);
114
115 static void write_vls_length_table (struct sfm_writer *w,
116                               const struct dictionary *dict);
117
118 static void write_long_string_value_labels (struct sfm_writer *,
119                                             const struct dictionary *);
120 static void write_long_string_missing_values (struct sfm_writer *,
121                                               const struct dictionary *);
122
123 static void write_mrsets (struct sfm_writer *, const struct dictionary *,
124                           bool pre_v14);
125
126 static void write_variable_display_parameters (struct sfm_writer *w,
127                                                const struct dictionary *dict);
128
129 static void write_documents (struct sfm_writer *, const struct dictionary *);
130
131 static void write_data_file_attributes (struct sfm_writer *,
132                                         const struct dictionary *);
133 static void write_variable_attributes (struct sfm_writer *,
134                                        const struct dictionary *);
135
136 static void write_int (struct sfm_writer *, int32_t);
137 static inline void convert_double_to_output_format (double, uint8_t[8]);
138 static void write_float (struct sfm_writer *, double);
139 static void write_string (struct sfm_writer *, const char *, size_t);
140 static void write_utf8_string (struct sfm_writer *, const char *encoding,
141                                const char *string, size_t width);
142 static void write_utf8_record (struct sfm_writer *, const char *encoding,
143                                const struct string *content, int subtype);
144 static void write_string_record (struct sfm_writer *,
145                                  const struct substring content, int subtype);
146 static void write_bytes (struct sfm_writer *, const void *, size_t);
147 static void write_zeros (struct sfm_writer *, size_t);
148 static void write_spaces (struct sfm_writer *, size_t);
149 static void write_value (struct sfm_writer *, const union value *, int width);
150
151 static void write_case_uncompressed (struct sfm_writer *,
152                                      const struct ccase *);
153 static void write_case_compressed (struct sfm_writer *, const struct ccase *);
154 static void flush_compressed (struct sfm_writer *);
155 static void put_cmp_opcode (struct sfm_writer *, uint8_t);
156 static void put_cmp_number (struct sfm_writer *, double);
157 static void put_cmp_string (struct sfm_writer *, const void *, size_t);
158
159 static bool write_error (const struct sfm_writer *);
160 static bool close_writer (struct sfm_writer *);
161
162 /* Returns default options for writing a system file. */
163 struct sfm_write_options
164 sfm_writer_default_options (void)
165 {
166   struct sfm_write_options opts;
167   opts.create_writeable = true;
168   opts.compress = settings_get_scompression ();
169   opts.version = 3;
170   return opts;
171 }
172
173 /* Opens the system file designated by file handle FH for writing
174    cases from dictionary D according to the given OPTS.
175
176    No reference to D is retained, so it may be modified or
177    destroyed at will after this function returns.  D is not
178    modified by this function, except to assign short names. */
179 struct casewriter *
180 sfm_open_writer (struct file_handle *fh, struct dictionary *d,
181                  struct sfm_write_options opts)
182 {
183   struct encoding_info encoding_info;
184   struct sfm_writer *w;
185   mode_t mode;
186   int i;
187
188   /* Check version. */
189   if (opts.version != 2 && opts.version != 3)
190     {
191       msg (ME, _("Unknown system file version %d. Treating as version %d."),
192            opts.version, 3);
193       opts.version = 3;
194     }
195
196   /* Create and initialize writer. */
197   w = xmalloc (sizeof *w);
198   w->fh = fh_ref (fh);
199   w->lock = NULL;
200   w->file = NULL;
201   w->rf = NULL;
202
203   w->compress = opts.compress;
204   w->case_cnt = 0;
205
206   w->n_opcodes = w->n_elements = 0;
207   memset (w->cbuf[0], 0, 8);
208
209   /* Figure out how to map in-memory case data to on-disk case
210      data.  Also count the number of segments.  Very long strings
211      occupy multiple segments, otherwise each variable only takes
212      one segment. */
213   w->segment_cnt = sfm_dictionary_to_sfm_vars (d, &w->sfm_vars,
214                                                &w->sfm_var_cnt);
215
216   /* Open file handle as an exclusive writer. */
217   /* TRANSLATORS: this fragment will be interpolated into
218      messages in fh_lock() that identify types of files. */
219   w->lock = fh_lock (fh, FH_REF_FILE, N_("system file"), FH_ACC_WRITE, true);
220   if (w->lock == NULL)
221     goto error;
222
223   /* Create the file on disk. */
224   mode = 0444;
225   if (opts.create_writeable)
226     mode |= 0222;
227   w->rf = replace_file_start (fh_get_file_name (fh), "wb", mode,
228                               &w->file, NULL);
229   if (w->rf == NULL)
230     {
231       msg (ME, _("Error opening `%s' for writing as a system file: %s."),
232            fh_get_file_name (fh), strerror (errno));
233       goto error;
234     }
235
236   get_encoding_info (&encoding_info, dict_get_encoding (d));
237   w->space = encoding_info.space[0];
238
239   /* Write the file header. */
240   write_header (w, d);
241
242   /* Write basic variable info. */
243   short_names_assign (d);
244   for (i = 0; i < dict_get_var_cnt (d); i++)
245     write_variable (w, dict_get_var (d, i));
246
247   write_value_labels (w, d);
248
249   if (dict_get_document_line_cnt (d) > 0)
250     write_documents (w, d);
251
252   write_integer_info_record (w, d);
253   write_float_info_record (w);
254
255   write_mrsets (w, d, true);
256
257   write_variable_display_parameters (w, d);
258
259   if (opts.version >= 3)
260     write_longvar_table (w, d);
261
262   write_vls_length_table (w, d);
263
264   write_long_string_value_labels (w, d);
265   write_long_string_missing_values (w, d);
266
267   if (opts.version >= 3)
268     {
269       if (attrset_count (dict_get_attributes (d)))
270         write_data_file_attributes (w, d);
271       write_variable_attributes (w, d);
272     }
273
274   write_mrsets (w, d, false);
275
276   write_encoding_record (w, d);
277
278   /* Write end-of-headers record. */
279   write_int (w, 999);
280   write_int (w, 0);
281
282   if (write_error (w))
283     goto error;
284
285   return casewriter_create (dict_get_proto (d), &sys_file_casewriter_class, w);
286
287 error:
288   close_writer (w);
289   return NULL;
290 }
291
292 /* Returns value of X truncated to two least-significant digits. */
293 static int
294 rerange (int x)
295 {
296   if (x < 0)
297     x = -x;
298   if (x >= 100)
299     x %= 100;
300   return x;
301 }
302
303 /* Calculates the offset of data for TARGET_VAR from the
304    beginning of each case's data for dictionary D.  The return
305    value is in "octs" (8-byte units). */
306 static int
307 calc_oct_idx (const struct dictionary *d, struct variable *target_var)
308 {
309   int oct_idx;
310   int i;
311
312   oct_idx = 0;
313   for (i = 0; i < dict_get_var_cnt (d); i++)
314     {
315       struct variable *var = dict_get_var (d, i);
316       if (var == target_var)
317         break;
318       oct_idx += sfm_width_to_octs (var_get_width (var));
319     }
320   return oct_idx;
321 }
322
323 /* Write the sysfile_header header to system file W. */
324 static void
325 write_header (struct sfm_writer *w, const struct dictionary *d)
326 {
327   const char *dict_encoding = dict_get_encoding (d);
328   char prod_name[61];
329   char creation_date[10];
330   char creation_time[9];
331   const char *file_label;
332   struct variable *weight;
333
334   time_t t;
335
336   /* Record-type code. */
337   if (is_encoding_ebcdic_compatible (dict_encoding))
338     write_string (w, EBCDIC_MAGIC, 4);
339   else
340     write_string (w, ASCII_MAGIC, 4);
341
342   /* Product identification. */
343   snprintf (prod_name, sizeof prod_name, "@(#) SPSS DATA FILE %s - %s",
344             version, host_system);
345   write_utf8_string (w, dict_encoding, prod_name, 60);
346
347   /* Layout code. */
348   write_int (w, 2);
349
350   /* Number of `union value's per case. */
351   write_int (w, calc_oct_idx (d, NULL));
352
353   /* Compressed? */
354   write_int (w, w->compress);
355
356   /* Weight variable. */
357   weight = dict_get_weight (d);
358   write_int (w, weight != NULL ? calc_oct_idx (d, weight) + 1 : 0);
359
360   /* Number of cases.  We don't know this in advance, so we write
361      -1 to indicate an unknown number of cases.  Later we can
362      come back and overwrite it with the true value. */
363   write_int (w, -1);
364
365   /* Compression bias. */
366   write_float (w, COMPRESSION_BIAS);
367
368   /* Creation date and time. */
369   if (time (&t) == (time_t) -1)
370     {
371       strcpy (creation_date, "01 Jan 70");
372       strcpy (creation_time, "00:00:00");
373     }
374   else
375     {
376       static const char *const month_name[12] =
377         {
378           "Jan", "Feb", "Mar", "Apr", "May", "Jun",
379           "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
380         };
381       struct tm *tmp = localtime (&t);
382       int day = rerange (tmp->tm_mday);
383       int mon = rerange (tmp->tm_mon + 1);
384       int year = rerange (tmp->tm_year);
385       int hour = rerange (tmp->tm_hour + 1);
386       int min = rerange (tmp->tm_min + 1);
387       int sec = rerange (tmp->tm_sec + 1);
388
389       snprintf (creation_date, sizeof creation_date,
390                 "%02d %s %02d", day, month_name[mon - 1], year);
391       snprintf (creation_time, sizeof creation_time,
392                 "%02d:%02d:%02d", hour - 1, min - 1, sec - 1);
393     }
394   write_utf8_string (w, dict_encoding, creation_date, 9);
395   write_utf8_string (w, dict_encoding, creation_time, 8);
396
397   /* File label. */
398   file_label = dict_get_label (d);
399   if (file_label == NULL)
400     file_label = "";
401   write_utf8_string (w, dict_encoding, file_label, 64);
402
403   /* Padding. */
404   write_zeros (w, 3);
405 }
406
407 /* Write format spec FMT to W, after adjusting it to be
408    compatible with the given WIDTH. */
409 static void
410 write_format (struct sfm_writer *w, struct fmt_spec fmt, int width)
411 {
412   assert (fmt_check_output (&fmt));
413   assert (sfm_width_to_segments (width) == 1);
414
415   if (width > 0)
416     fmt_resize (&fmt, width);
417   write_int (w, (fmt_to_io (fmt.type) << 16) | (fmt.w << 8) | fmt.d);
418 }
419
420 /* Write a string continuation variable record for each 8-byte
421    section beyond the initial 8 bytes, for a variable of the
422    given WIDTH. */
423 static void
424 write_variable_continuation_records (struct sfm_writer *w, int width)
425 {
426   int position;
427
428   assert (sfm_width_to_segments (width) == 1);
429   for (position = 8; position < width; position += 8)
430     {
431       write_int (w, 2);   /* Record type. */
432       write_int (w, -1);  /* Width. */
433       write_int (w, 0);   /* No variable label. */
434       write_int (w, 0);   /* No missing values. */
435       write_int (w, 0);   /* Print format. */
436       write_int (w, 0);   /* Write format. */
437       write_zeros (w, 8);   /* Name. */
438     }
439 }
440
441 /* Write the variable record(s) for variable V to system file
442    W. */
443 static void
444 write_variable (struct sfm_writer *w, const struct variable *v)
445 {
446   int width = var_get_width (v);
447   int segment_cnt = sfm_width_to_segments (width);
448   int seg0_width = sfm_segment_alloc_width (width, 0);
449   const char *encoding = var_get_encoding (v);
450   int i;
451
452   /* Record type. */
453   write_int (w, 2);
454
455   /* Width. */
456   write_int (w, seg0_width);
457
458   /* Variable has a variable label? */
459   write_int (w, var_has_label (v));
460
461   /* Number of missing values.  If there is a range, then the
462      range counts as 2 missing values and causes the number to be
463      negated.
464
465      Missing values for long string variables are written in a separate
466      record. */
467   if (width <= MAX_SHORT_STRING)
468     {
469       const struct missing_values *mv = var_get_missing_values (v);
470       if (mv_has_range (mv))
471         write_int (w, -2 - mv_n_values (mv));
472       else
473         write_int (w, mv_n_values (mv));
474     }
475   else
476     write_int (w, 0);
477
478   /* Print and write formats. */
479   write_format (w, *var_get_print_format (v), seg0_width);
480   write_format (w, *var_get_write_format (v), seg0_width);
481
482   /* Short name.
483      The full name is in a translation table written
484      separately. */
485   write_utf8_string (w, encoding, var_get_short_name (v, 0), 8);
486
487   /* Value label. */
488   if (var_has_label (v))
489     {
490       char *label = recode_string (encoding, UTF8, var_get_label (v), -1);
491       size_t label_len = MIN (strlen (label), 255);
492       size_t padded_len = ROUND_UP (label_len, 4);
493       write_int (w, label_len);
494       write_string (w, label, padded_len);
495       free (label);
496     }
497
498   /* Write the missing values, if any, range first. */
499   if (width <= MAX_SHORT_STRING)
500     {
501       const struct missing_values *mv = var_get_missing_values (v);
502       if (mv_has_range (mv))
503         {
504           double x, y;
505           mv_get_range (mv, &x, &y);
506           write_float (w, x);
507           write_float (w, y);
508         }
509       for (i = 0; i < mv_n_values (mv); i++)
510         write_value (w, mv_get_value (mv, i), width);
511     }
512
513   write_variable_continuation_records (w, seg0_width);
514
515   /* Write additional segments for very long string variables. */
516   for (i = 1; i < segment_cnt; i++)
517     {
518       int seg_width = sfm_segment_alloc_width (width, i);
519       struct fmt_spec fmt = fmt_for_output (FMT_A, MAX (seg_width, 1), 0);
520
521       write_int (w, 2);           /* Variable record. */
522       write_int (w, seg_width);   /* Width. */
523       write_int (w, 0);           /* No variable label. */
524       write_int (w, 0);           /* No missing values. */
525       write_format (w, fmt, seg_width); /* Print format. */
526       write_format (w, fmt, seg_width); /* Write format. */
527       write_utf8_string (w, encoding, var_get_short_name (v, i), 8);
528
529       write_variable_continuation_records (w, seg_width);
530     }
531 }
532
533 /* Writes the value labels to system file W.
534
535    Value labels for long string variables are written separately,
536    by write_long_string_value_labels. */
537 static void
538 write_value_labels (struct sfm_writer *w, const struct dictionary *d)
539 {
540   struct label_set
541     {
542       struct hmap_node hmap_node;
543       const struct val_labs *val_labs;
544       int *indexes;
545       size_t n_indexes, allocated_indexes;
546     };
547
548   size_t n_sets, allocated_sets;
549   struct label_set **sets;
550   struct hmap same_sets;
551   size_t i;
552   int idx;
553
554   n_sets = allocated_sets = 0;
555   sets = NULL;
556   hmap_init (&same_sets);
557
558   idx = 0;
559   for (i = 0; i < dict_get_var_cnt (d); i++)
560     {
561       struct variable *v = dict_get_var (d, i);
562
563       if (var_has_value_labels (v) && var_get_width (v) <= 8)
564         {
565           const struct val_labs *val_labs = var_get_value_labels (v);
566           unsigned int hash = val_labs_hash (val_labs, 0);
567           struct label_set *set;
568
569           HMAP_FOR_EACH_WITH_HASH (set, struct label_set, hmap_node,
570                                    hash, &same_sets)
571             {
572               if (val_labs_equal (set->val_labs, val_labs))
573                 {
574                   if (set->n_indexes >= set->allocated_indexes)
575                     set->indexes = x2nrealloc (set->indexes,
576                                                &set->allocated_indexes,
577                                                sizeof *set->indexes);
578                   set->indexes[set->n_indexes++] = idx;
579                   goto next_var;
580                 }
581             }
582
583           set = xmalloc (sizeof *set);
584           set->val_labs = val_labs;
585           set->indexes = xmalloc (sizeof *set->indexes);
586           set->indexes[0] = idx;
587           set->n_indexes = 1;
588           set->allocated_indexes = 1;
589           hmap_insert (&same_sets, &set->hmap_node, hash);
590
591           if (n_sets >= allocated_sets)
592             sets = x2nrealloc (sets, &allocated_sets, sizeof *sets);
593           sets[n_sets++] = set;
594         }
595
596     next_var:
597       idx += sfm_width_to_octs (var_get_width (v));
598     }
599
600   for (i = 0; i < n_sets; i++)
601     {
602       const struct label_set *set = sets[i];
603       const struct val_labs *val_labs = set->val_labs;
604       size_t n_labels = val_labs_count (val_labs);
605       int width = val_labs_get_width (val_labs);
606       const struct val_lab **labels;
607       size_t j;
608
609       /* Value label record. */
610       write_int (w, 3);             /* Record type. */
611       write_int (w, n_labels);
612       labels = val_labs_sorted (val_labs);
613       for (j = 0; j < n_labels; j++)
614         {
615           const struct val_lab *vl = labels[j];
616           char *label = recode_string (dict_get_encoding (d), UTF8,
617                                        val_lab_get_escaped_label (vl), -1);
618           uint8_t len = MIN (strlen (label), 255);
619
620           write_value (w, val_lab_get_value (vl), width);
621           write_bytes (w, &len, 1);
622           write_bytes (w, label, len);
623           write_zeros (w, REM_RND_UP (len + 1, 8));
624           free (label);
625         }
626       free (labels);
627
628       /* Value label variable record. */
629       write_int (w, 4);              /* Record type. */
630       write_int (w, set->n_indexes);
631       for (j = 0; j < set->n_indexes; j++)
632         write_int (w, set->indexes[j] + 1);
633     }
634
635   for (i = 0; i < n_sets; i++)
636     {
637       struct label_set *set = sets[i];
638
639       free (set->indexes);
640       free (set);
641     }
642   free (sets);
643   hmap_destroy (&same_sets);
644 }
645
646 /* Writes record type 6, document record. */
647 static void
648 write_documents (struct sfm_writer *w, const struct dictionary *d)
649 {
650   const struct string_array *docs = dict_get_documents (d);
651   const char *enc = dict_get_encoding (d);
652   size_t i;
653
654   write_int (w, 6);             /* Record type. */
655   write_int (w, docs->n);
656   for (i = 0; i < docs->n; i++)
657     {
658       char *s = recode_string (enc, "UTF-8", docs->strings[i], -1);
659       size_t s_len = strlen (s);
660       size_t write_len = MIN (s_len, DOC_LINE_LENGTH);
661
662       write_bytes (w, s, write_len);
663       write_spaces (w, DOC_LINE_LENGTH - write_len);
664       free (s);
665     }
666 }
667
668 static void
669 put_attrset (struct string *string, const struct attrset *attrs)
670 {
671   const struct attribute *attr;
672   struct attrset_iterator i;
673
674   for (attr = attrset_first (attrs, &i); attr != NULL;
675        attr = attrset_next (attrs, &i)) 
676     {
677       size_t n_values = attribute_get_n_values (attr);
678       size_t j;
679
680       ds_put_cstr (string, attribute_get_name (attr));
681       ds_put_byte (string, '(');
682       for (j = 0; j < n_values; j++) 
683         ds_put_format (string, "'%s'\n", attribute_get_value (attr, j));
684       ds_put_byte (string, ')');
685     }
686 }
687
688 static void
689 write_data_file_attributes (struct sfm_writer *w,
690                             const struct dictionary *d)
691 {
692   struct string s = DS_EMPTY_INITIALIZER;
693   put_attrset (&s, dict_get_attributes (d));
694   write_utf8_record (w, dict_get_encoding (d), &s, 17);
695   ds_destroy (&s);
696 }
697
698 static void
699 add_role_attribute (enum var_role role, struct attrset *attrs)
700 {
701   struct attribute *attr;
702   const char *s;
703
704   switch (role)
705     {
706     case ROLE_INPUT:
707     default:
708       s = "0";
709       break;
710
711     case ROLE_TARGET:
712       s = "1";
713       break;
714
715     case ROLE_BOTH:
716       s = "2";
717       break;
718
719     case ROLE_NONE:
720       s = "3";
721       break;
722
723     case ROLE_PARTITION:
724       s = "4";
725       break;
726
727     case ROLE_SPLIT:
728       s = "5";
729       break;
730     }
731   attrset_delete (attrs, "$@Role");
732
733   attr = attribute_create ("$@Role");
734   attribute_add_value (attr, s);
735   attrset_add (attrs, attr);
736 }
737
738 static void
739 write_variable_attributes (struct sfm_writer *w, const struct dictionary *d)
740 {
741   struct string s = DS_EMPTY_INITIALIZER;
742   size_t n_vars = dict_get_var_cnt (d);
743   size_t n_attrsets = 0;
744   size_t i;
745
746   for (i = 0; i < n_vars; i++)
747     { 
748       struct variable *v = dict_get_var (d, i);
749       struct attrset attrs;
750
751       attrset_clone (&attrs, var_get_attributes (v));
752
753       add_role_attribute (var_get_role (v), &attrs);
754       if (n_attrsets++)
755         ds_put_byte (&s, '/');
756       ds_put_format (&s, "%s:", var_get_name (v));
757       put_attrset (&s, &attrs);
758       attrset_destroy (&attrs);
759     }
760   if (n_attrsets)
761     write_utf8_record (w, dict_get_encoding (d), &s, 18);
762   ds_destroy (&s);
763 }
764
765 /* Write multiple response sets.  If PRE_V14 is true, writes sets supported by
766    SPSS before release 14, otherwise writes sets supported only by later
767    versions. */
768 static void
769 write_mrsets (struct sfm_writer *w, const struct dictionary *dict,
770               bool pre_v14)
771 {
772   const char *encoding = dict_get_encoding (dict);
773   struct string s = DS_EMPTY_INITIALIZER;
774   size_t n_mrsets;
775   size_t i;
776
777   if (is_encoding_ebcdic_compatible (encoding))
778     {
779       /* FIXME. */
780       return;
781     }
782
783   n_mrsets = dict_get_n_mrsets (dict);
784   if (n_mrsets == 0)
785     return;
786
787   for (i = 0; i < n_mrsets; i++)
788     {
789       const struct mrset *mrset = dict_get_mrset (dict, i);
790       char *name;
791       size_t j;
792
793       if ((mrset->type != MRSET_MD || mrset->cat_source != MRSET_COUNTEDVALUES)
794           != pre_v14)
795         continue;
796
797       name = recode_string (encoding, "UTF-8", mrset->name, -1);
798       ds_put_format (&s, "%s=", name);
799       free (name);
800
801       if (mrset->type == MRSET_MD)
802         {
803           char *counted;
804
805           if (mrset->cat_source == MRSET_COUNTEDVALUES)
806             ds_put_format (&s, "E %d ", mrset->label_from_var_label ? 11 : 1);
807           else
808             ds_put_byte (&s, 'D');
809
810           if (mrset->width == 0)
811             counted = xasprintf ("%.0f", mrset->counted.f);
812           else
813             counted = xmemdup0 (value_str (&mrset->counted, mrset->width),
814                                 mrset->width);
815           ds_put_format (&s, "%zu %s", strlen (counted), counted);
816           free (counted);
817         }
818       else
819         ds_put_byte (&s, 'C');
820       ds_put_byte (&s, ' ');
821
822       if (mrset->label && !mrset->label_from_var_label)
823         {
824           char *label = recode_string (encoding, "UTF-8", mrset->label, -1);
825           ds_put_format (&s, "%zu %s", strlen (label), label);
826           free (label);
827         }
828       else
829         ds_put_cstr (&s, "0 ");
830
831       for (j = 0; j < mrset->n_vars; j++)
832         {
833           const char *short_name_utf8 = var_get_short_name (mrset->vars[j], 0);
834           char *lower_name_utf8 = utf8_to_lower (short_name_utf8);
835           char *short_name = recode_string (encoding, "UTF-8",
836                                             lower_name_utf8, -1);
837           ds_put_format (&s, " %s", short_name);
838           free (short_name);
839           free (lower_name_utf8);
840         }
841       ds_put_byte (&s, '\n');
842     }
843
844   if (!ds_is_empty (&s))
845     write_string_record (w, ds_ss (&s), pre_v14 ? 7 : 19);
846   ds_destroy (&s);
847 }
848
849 /* Write the alignment, width and scale values. */
850 static void
851 write_variable_display_parameters (struct sfm_writer *w,
852                                    const struct dictionary *dict)
853 {
854   int i;
855
856   write_int (w, 7);             /* Record type. */
857   write_int (w, 11);            /* Record subtype. */
858   write_int (w, 4);             /* Data item (int32) size. */
859   write_int (w, w->segment_cnt * 3); /* Number of data items. */
860
861   for (i = 0; i < dict_get_var_cnt (dict); ++i)
862     {
863       struct variable *v = dict_get_var (dict, i);
864       int width = var_get_width (v);
865       int segment_cnt = sfm_width_to_segments (width);
866       int measure = (var_get_measure (v) == MEASURE_NOMINAL ? 1
867                      : var_get_measure (v) == MEASURE_ORDINAL ? 2
868                      : 3);
869       int alignment = (var_get_alignment (v) == ALIGN_LEFT ? 0
870                        : var_get_alignment (v) == ALIGN_RIGHT ? 1
871                        : 2);
872       int i;
873
874       for (i = 0; i < segment_cnt; i++)
875         {
876           int width_left = width - sfm_segment_effective_offset (width, i);
877           write_int (w, measure);
878           write_int (w, (i == 0 ? var_get_display_width (v)
879                          : var_default_display_width (width_left)));
880           write_int (w, alignment);
881         }
882     }
883 }
884
885 /* Writes the table of lengths for very long string variables. */
886 static void
887 write_vls_length_table (struct sfm_writer *w,
888                         const struct dictionary *dict)
889 {
890   struct string map;
891   int i;
892
893   ds_init_empty (&map);
894   for (i = 0; i < dict_get_var_cnt (dict); ++i)
895     {
896       const struct variable *v = dict_get_var (dict, i);
897       if (sfm_width_to_segments (var_get_width (v)) > 1)
898         ds_put_format (&map, "%s=%05d%c\t",
899                        var_get_short_name (v, 0), var_get_width (v), 0);
900     }
901   if (!ds_is_empty (&map))
902     write_utf8_record (w, dict_get_encoding (dict), &map, 14);
903   ds_destroy (&map);
904 }
905
906 static void
907 write_long_string_value_labels (struct sfm_writer *w,
908                                 const struct dictionary *dict)
909 {
910   const char *encoding = dict_get_encoding (dict);
911   size_t n_vars = dict_get_var_cnt (dict);
912   size_t size, i;
913   off_t start UNUSED;
914
915   /* Figure out the size in advance. */
916   size = 0;
917   for (i = 0; i < n_vars; i++)
918     {
919       struct variable *var = dict_get_var (dict, i);
920       const struct val_labs *val_labs = var_get_value_labels (var);
921       int width = var_get_width (var);
922       const struct val_lab *val_lab;
923
924       if (val_labs_count (val_labs) == 0 || width < 9)
925         continue;
926
927       size += 12;
928       size += recode_string_len (encoding, "UTF-8", var_get_name (var), -1);
929       for (val_lab = val_labs_first (val_labs); val_lab != NULL;
930            val_lab = val_labs_next (val_labs, val_lab))
931         {
932           size += 8 + width;
933           size += recode_string_len (encoding, "UTF-8",
934                                      val_lab_get_escaped_label (val_lab), -1);
935         }
936     }
937   if (size == 0)
938     return;
939
940   write_int (w, 7);             /* Record type. */
941   write_int (w, 21);            /* Record subtype */
942   write_int (w, 1);             /* Data item (byte) size. */
943   write_int (w, size);          /* Number of data items. */
944
945   start = ftello (w->file);
946   for (i = 0; i < n_vars; i++)
947     {
948       struct variable *var = dict_get_var (dict, i);
949       const struct val_labs *val_labs = var_get_value_labels (var);
950       int width = var_get_width (var);
951       const struct val_lab *val_lab;
952       char *var_name;
953
954       if (val_labs_count (val_labs) == 0 || width < 9)
955         continue;
956
957       var_name = recode_string (encoding, "UTF-8", var_get_name (var), -1);
958       write_int (w, strlen (var_name));
959       write_bytes (w, var_name, strlen (var_name));
960       free (var_name);
961
962       write_int (w, width);
963       write_int (w, val_labs_count (val_labs));
964       for (val_lab = val_labs_first (val_labs); val_lab != NULL;
965            val_lab = val_labs_next (val_labs, val_lab))
966         {
967           char *label;
968           size_t len;
969
970           write_int (w, width);
971           write_bytes (w, value_str (val_lab_get_value (val_lab), width),
972                        width);
973
974           label = recode_string (var_get_encoding (var), "UTF-8",
975                                  val_lab_get_escaped_label (val_lab), -1);
976           len = strlen (label);
977           write_int (w, len);
978           write_bytes (w, label, len);
979           free (label);
980         }
981     }
982   assert (ftello (w->file) == start + size);
983 }
984
985 static void
986 write_long_string_missing_values (struct sfm_writer *w,
987                                   const struct dictionary *dict)
988 {
989   const char *encoding = dict_get_encoding (dict);
990   size_t n_vars = dict_get_var_cnt (dict);
991   size_t size, i;
992   off_t start UNUSED;
993
994   /* Figure out the size in advance. */
995   size = 0;
996   for (i = 0; i < n_vars; i++)
997     {
998       struct variable *var = dict_get_var (dict, i);
999       const struct missing_values *mv = var_get_missing_values (var);
1000       int width = var_get_width (var);
1001
1002       if (mv_is_empty (mv) || width < 9)
1003         continue;
1004
1005       size += 4;
1006       size += recode_string_len (encoding, "UTF-8", var_get_name (var), -1);
1007       size += 1;
1008       size += mv_n_values (mv) * (4 + 8);
1009     }
1010   if (size == 0)
1011     return;
1012
1013   write_int (w, 7);             /* Record type. */
1014   write_int (w, 22);            /* Record subtype */
1015   write_int (w, 1);             /* Data item (byte) size. */
1016   write_int (w, size);          /* Number of data items. */
1017
1018   start = ftello (w->file);
1019   for (i = 0; i < n_vars; i++)
1020     {
1021       struct variable *var = dict_get_var (dict, i);
1022       const struct missing_values *mv = var_get_missing_values (var);
1023       int width = var_get_width (var);
1024       uint8_t n_missing_values;
1025       char *var_name;
1026       int j;
1027
1028       if (mv_is_empty (mv) || width < 9)
1029         continue;
1030
1031       var_name = recode_string (encoding, "UTF-8", var_get_name (var), -1);
1032       write_int (w, strlen (var_name));
1033       write_bytes (w, var_name, strlen (var_name));
1034       free (var_name);
1035
1036       n_missing_values = mv_n_values (mv);
1037       write_bytes (w, &n_missing_values, 1);
1038
1039       for (j = 0; j < n_missing_values; j++)
1040         {
1041           const union value *value = mv_get_value (mv, j);
1042
1043           write_int (w, 8);
1044           write_bytes (w, value_str (value, width), 8);
1045         }
1046     }
1047   assert (ftello (w->file) == start + size);
1048 }
1049
1050 static void
1051 write_encoding_record (struct sfm_writer *w,
1052                        const struct dictionary *d)
1053 {
1054   /* IANA says "...character set names may be up to 40 characters taken
1055      from the printable characters of US-ASCII," so character set names
1056      don't need to be recoded to be in UTF-8.
1057
1058      We convert encoding names to uppercase because SPSS writes encoding
1059      names in uppercase. */
1060   char *encoding = xstrdup (dict_get_encoding (d));
1061   str_uppercase (encoding);
1062   write_string_record (w, ss_cstr (encoding), 20);
1063   free (encoding);
1064 }
1065
1066 /* Writes the long variable name table. */
1067 static void
1068 write_longvar_table (struct sfm_writer *w, const struct dictionary *dict)
1069 {
1070   struct string map;
1071   size_t i;
1072
1073   ds_init_empty (&map);
1074   for (i = 0; i < dict_get_var_cnt (dict); i++)
1075     {
1076       struct variable *v = dict_get_var (dict, i);
1077       if (i)
1078         ds_put_byte (&map, '\t');
1079       ds_put_format (&map, "%s=%s",
1080                      var_get_short_name (v, 0), var_get_name (v));
1081     }
1082   write_utf8_record (w, dict_get_encoding (dict), &map, 13);
1083   ds_destroy (&map);
1084 }
1085
1086 /* Write integer information record. */
1087 static void
1088 write_integer_info_record (struct sfm_writer *w,
1089                            const struct dictionary *d)
1090 {
1091   const char *dict_encoding = dict_get_encoding (d);
1092   int version_component[3];
1093   int float_format;
1094   int codepage;
1095
1096   /* Parse the version string. */
1097   memset (version_component, 0, sizeof version_component);
1098   sscanf (bare_version, "%d.%d.%d",
1099           &version_component[0], &version_component[1], &version_component[2]);
1100
1101   /* Figure out the floating-point format. */
1102   if (FLOAT_NATIVE_64_BIT == FLOAT_IEEE_DOUBLE_LE
1103       || FLOAT_NATIVE_64_BIT == FLOAT_IEEE_DOUBLE_BE)
1104     float_format = 1;
1105   else if (FLOAT_NATIVE_64_BIT == FLOAT_Z_LONG)
1106     float_format = 2;
1107   else if (FLOAT_NATIVE_64_BIT == FLOAT_VAX_D)
1108     float_format = 3;
1109   else
1110     abort ();
1111
1112   /* Choose codepage. */
1113   codepage = sys_get_codepage_from_encoding (dict_encoding);
1114   if (codepage == 0)
1115     {
1116       /* The codepage is unknown.  Choose a default.
1117
1118          For an EBCDIC-compatible encoding, use the value for EBCDIC.
1119
1120          For an ASCII-compatible encoding, default to "7-bit ASCII", because
1121          many files use this codepage number regardless of their actual
1122          encoding.
1123       */
1124       if (is_encoding_ascii_compatible (dict_encoding))
1125         codepage = 2;
1126       else if (is_encoding_ebcdic_compatible (dict_encoding))
1127         codepage = 1;
1128     }
1129
1130   /* Write record. */
1131   write_int (w, 7);             /* Record type. */
1132   write_int (w, 3);             /* Record subtype. */
1133   write_int (w, 4);             /* Data item (int32) size. */
1134   write_int (w, 8);             /* Number of data items. */
1135   write_int (w, version_component[0]);
1136   write_int (w, version_component[1]);
1137   write_int (w, version_component[2]);
1138   write_int (w, -1);          /* Machine code. */
1139   write_int (w, float_format);
1140   write_int (w, 1);           /* Compression code. */
1141   write_int (w, INTEGER_NATIVE == INTEGER_MSB_FIRST ? 1 : 2);
1142   write_int (w, codepage);
1143 }
1144
1145 /* Write floating-point information record. */
1146 static void
1147 write_float_info_record (struct sfm_writer *w)
1148 {
1149   write_int (w, 7);             /* Record type. */
1150   write_int (w, 4);             /* Record subtype. */
1151   write_int (w, 8);             /* Data item (flt64) size. */
1152   write_int (w, 3);             /* Number of data items. */
1153   write_float (w, SYSMIS);      /* System-missing value. */
1154   write_float (w, HIGHEST);     /* Value used for HIGHEST in missing values. */
1155   write_float (w, LOWEST);      /* Value used for LOWEST in missing values. */
1156 }
1157 \f
1158 /* Writes case C to system file W. */
1159 static void
1160 sys_file_casewriter_write (struct casewriter *writer, void *w_,
1161                            struct ccase *c)
1162 {
1163   struct sfm_writer *w = w_;
1164
1165   if (ferror (w->file))
1166     {
1167       casewriter_force_error (writer);
1168       case_unref (c);
1169       return;
1170     }
1171
1172   w->case_cnt++;
1173
1174   if (!w->compress)
1175     write_case_uncompressed (w, c);
1176   else
1177     write_case_compressed (w, c);
1178
1179   case_unref (c);
1180 }
1181
1182 /* Destroys system file writer W. */
1183 static void
1184 sys_file_casewriter_destroy (struct casewriter *writer, void *w_)
1185 {
1186   struct sfm_writer *w = w_;
1187   if (!close_writer (w))
1188     casewriter_force_error (writer);
1189 }
1190
1191 /* Returns true if an I/O error has occurred on WRITER, false otherwise. */
1192 static bool
1193 write_error (const struct sfm_writer *writer)
1194 {
1195   return ferror (writer->file);
1196 }
1197
1198 /* Closes a system file after we're done with it.
1199    Returns true if successful, false if an I/O error occurred. */
1200 static bool
1201 close_writer (struct sfm_writer *w)
1202 {
1203   bool ok;
1204
1205   if (w == NULL)
1206     return true;
1207
1208   ok = true;
1209   if (w->file != NULL)
1210     {
1211       /* Flush buffer. */
1212       flush_compressed (w);
1213       fflush (w->file);
1214
1215       ok = !write_error (w);
1216
1217       /* Seek back to the beginning and update the number of cases.
1218          This is just a courtesy to later readers, so there's no need
1219          to check return values or report errors. */
1220       if (ok && w->case_cnt <= INT32_MAX && !fseeko (w->file, 80, SEEK_SET))
1221         {
1222           write_int (w, w->case_cnt);
1223           clearerr (w->file);
1224         }
1225
1226       if (fclose (w->file) == EOF)
1227         ok = false;
1228
1229       if (!ok)
1230         msg (ME, _("An I/O error occurred writing system file `%s'."),
1231              fh_get_file_name (w->fh));
1232
1233       if (ok ? !replace_file_commit (w->rf) : !replace_file_abort (w->rf))
1234         ok = false;
1235     }
1236
1237   fh_unlock (w->lock);
1238   fh_unref (w->fh);
1239
1240   free (w->sfm_vars);
1241   free (w);
1242
1243   return ok;
1244 }
1245
1246 /* System file writer casewriter class. */
1247 static const struct casewriter_class sys_file_casewriter_class =
1248   {
1249     sys_file_casewriter_write,
1250     sys_file_casewriter_destroy,
1251     NULL,
1252   };
1253 \f
1254 /* Writes case C to system file W, without compressing it. */
1255 static void
1256 write_case_uncompressed (struct sfm_writer *w, const struct ccase *c)
1257 {
1258   size_t i;
1259
1260   for (i = 0; i < w->sfm_var_cnt; i++)
1261     {
1262       struct sfm_var *v = &w->sfm_vars[i];
1263
1264       if (v->var_width == 0)
1265         write_float (w, case_num_idx (c, v->case_index));
1266       else
1267         {
1268           write_bytes (w, case_str_idx (c, v->case_index) + v->offset,
1269                        v->segment_width);
1270           write_spaces (w, v->padding);
1271         }
1272     }
1273 }
1274
1275 /* Writes case C to system file W, with compression. */
1276 static void
1277 write_case_compressed (struct sfm_writer *w, const struct ccase *c)
1278 {
1279   size_t i;
1280
1281   for (i = 0; i < w->sfm_var_cnt; i++)
1282     {
1283       struct sfm_var *v = &w->sfm_vars[i];
1284
1285       if (v->var_width == 0)
1286         {
1287           double d = case_num_idx (c, v->case_index);
1288           if (d == SYSMIS)
1289             put_cmp_opcode (w, 255);
1290           else if (d >= 1 - COMPRESSION_BIAS
1291                    && d <= 251 - COMPRESSION_BIAS
1292                    && d == (int) d)
1293             put_cmp_opcode (w, (int) d + COMPRESSION_BIAS);
1294           else
1295             put_cmp_number (w, d);
1296         }
1297       else
1298         {
1299           int offset = v->offset;
1300           int width, padding;
1301
1302           /* This code properly deals with a width that is not a
1303              multiple of 8, by ensuring that the final partial
1304              oct (8 byte unit) is treated as padded with spaces
1305              on the right. */
1306           for (width = v->segment_width; width > 0; width -= 8, offset += 8)
1307             {
1308               const void *data = case_str_idx (c, v->case_index) + offset;
1309               int chunk_size = MIN (width, 8);
1310               if (!memcmp (data, "        ", chunk_size))
1311                 put_cmp_opcode (w, 254);
1312               else
1313                 put_cmp_string (w, data, chunk_size);
1314             }
1315
1316           /* This code deals properly with padding that is not a
1317              multiple of 8 bytes, by discarding the remainder,
1318              which was already effectively padded with spaces in
1319              the previous loop.  (Note that v->width + v->padding
1320              is always a multiple of 8.) */
1321           for (padding = v->padding / 8; padding > 0; padding--)
1322             put_cmp_opcode (w, 254);
1323         }
1324     }
1325 }
1326
1327 /* Flushes buffered compressed opcodes and data to W. */
1328 static void
1329 flush_compressed (struct sfm_writer *w)
1330 {
1331   if (w->n_opcodes)
1332     {
1333       write_bytes (w, w->cbuf, 8 * (1 + w->n_elements));
1334       w->n_opcodes = w->n_elements = 0;
1335       memset (w->cbuf[0], 0, 8);
1336     }
1337 }
1338
1339 /* Appends OPCODE to the buffered set of compression opcodes in
1340    W.  Flushes the compression buffer beforehand if necessary. */
1341 static void
1342 put_cmp_opcode (struct sfm_writer *w, uint8_t opcode)
1343 {
1344   if (w->n_opcodes >= 8)
1345     flush_compressed (w);
1346
1347   w->cbuf[0][w->n_opcodes++] = opcode;
1348 }
1349
1350 /* Appends NUMBER to the buffered compression data in W. */
1351 static void
1352 put_cmp_number (struct sfm_writer *w, double number)
1353 {
1354   put_cmp_opcode (w, 253);
1355   convert_double_to_output_format (number, w->cbuf[++w->n_elements]);
1356 }
1357
1358 /* Appends SIZE bytes of DATA to the buffered compression data in
1359    W, followed by enough spaces to pad the output data to exactly
1360    8 bytes (thus, SIZE must be no greater than 8). */
1361 static void
1362 put_cmp_string (struct sfm_writer *w, const void *data, size_t size)
1363 {
1364   assert (size <= 8);
1365
1366   put_cmp_opcode (w, 253);
1367   w->n_elements++;
1368   memset (w->cbuf[w->n_elements], w->space, 8);
1369   memcpy (w->cbuf[w->n_elements], data, size);
1370 }
1371 \f
1372 /* Writes 32-bit integer X to the output file for writer W. */
1373 static void
1374 write_int (struct sfm_writer *w, int32_t x)
1375 {
1376   write_bytes (w, &x, sizeof x);
1377 }
1378
1379 /* Converts NATIVE to the 64-bit format used in output files in
1380    OUTPUT. */
1381 static inline void
1382 convert_double_to_output_format (double native, uint8_t output[8])
1383 {
1384   /* If "double" is not a 64-bit type, then convert it to a
1385      64-bit type.  Otherwise just copy it. */
1386   if (FLOAT_NATIVE_DOUBLE != FLOAT_NATIVE_64_BIT)
1387     float_convert (FLOAT_NATIVE_DOUBLE, &native, FLOAT_NATIVE_64_BIT, output);
1388   else
1389     memcpy (output, &native, sizeof native);
1390 }
1391
1392 /* Writes floating-point number X to the output file for writer
1393    W. */
1394 static void
1395 write_float (struct sfm_writer *w, double x)
1396 {
1397   uint8_t output[8];
1398   convert_double_to_output_format (x, output);
1399   write_bytes (w, output, sizeof output);
1400 }
1401
1402 /* Writes contents of VALUE with the given WIDTH to W, padding
1403    with zeros to a multiple of 8 bytes.
1404    To avoid a branch, and because we don't actually need to
1405    support it, WIDTH must be no bigger than 8. */
1406 static void
1407 write_value (struct sfm_writer *w, const union value *value, int width)
1408 {
1409   assert (width <= 8);
1410   if (width == 0)
1411     write_float (w, value->f);
1412   else
1413     {
1414       write_bytes (w, value_str (value, width), width);
1415       write_zeros (w, 8 - width);
1416     }
1417 }
1418
1419 /* Writes null-terminated STRING in a field of the given WIDTH to W.  If STRING
1420    is longer than WIDTH, it is truncated; if STRING is shorter than WIDTH, it
1421    is padded on the right with spaces. */
1422 static void
1423 write_string (struct sfm_writer *w, const char *string, size_t width)
1424 {
1425   size_t data_bytes = MIN (strlen (string), width);
1426   size_t pad_bytes = width - data_bytes;
1427   write_bytes (w, string, data_bytes);
1428   while (pad_bytes-- > 0)
1429     putc (w->space, w->file);
1430 }
1431
1432 /* Recodes null-terminated UTF-8 encoded STRING into ENCODING, and writes the
1433    recoded version in a field of the given WIDTH to W.  The string is truncated
1434    or padded on the right with spaces to exactly WIDTH bytes. */
1435 static void
1436 write_utf8_string (struct sfm_writer *w, const char *encoding,
1437                    const char *string, size_t width)
1438 {
1439   char *s = recode_string (encoding, "UTF-8", string, -1);
1440   write_string (w, s, width);
1441   free (s);
1442 }
1443
1444 /* Writes a record with type 7, subtype SUBTYPE that contains CONTENT recoded
1445    from UTF-8 encoded into ENCODING. */
1446 static void
1447 write_utf8_record (struct sfm_writer *w, const char *encoding,
1448                    const struct string *content, int subtype)
1449 {
1450   struct substring s;
1451
1452   s = recode_substring_pool (encoding, "UTF-8", ds_ss (content), NULL);
1453   write_string_record (w, s, subtype);
1454   ss_dealloc (&s);
1455 }
1456
1457 /* Writes a record with type 7, subtype SUBTYPE that contains the string
1458    CONTENT. */
1459 static void
1460 write_string_record (struct sfm_writer *w,
1461                      const struct substring content, int subtype)
1462 {
1463   write_int (w, 7);
1464   write_int (w, subtype);
1465   write_int (w, 1);
1466   write_int (w, ss_length (content));
1467   write_bytes (w, ss_data (content), ss_length (content));
1468 }
1469
1470 /* Writes SIZE bytes of DATA to W's output file. */
1471 static void
1472 write_bytes (struct sfm_writer *w, const void *data, size_t size)
1473 {
1474   fwrite (data, 1, size, w->file);
1475 }
1476
1477 /* Writes N zeros to W's output file. */
1478 static void
1479 write_zeros (struct sfm_writer *w, size_t n)
1480 {
1481   while (n-- > 0)
1482     putc (0, w->file);
1483 }
1484
1485 /* Writes N spaces to W's output file. */
1486 static void
1487 write_spaces (struct sfm_writer *w, size_t n)
1488 {
1489   while (n-- > 0)
1490     putc (w->space, w->file);
1491 }