Imported Upstream version 0.18.3.2
[platform/upstream/gettext.git] / gettext-tools / src / write-mo.c
1 /* Writing binary .mo files.
2    Copyright (C) 1995-1998, 2000-2007 Free Software Foundation, Inc.
3    Written by Ulrich Drepper <drepper@gnu.ai.mit.edu>, April 1995.
4
5    This program is free software: you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation; either version 3 of the License, or
8    (at your option) any later version.
9
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
17
18 #ifdef HAVE_CONFIG_H
19 # include <config.h>
20 #endif
21 #include <alloca.h>
22
23 /* Specification.  */
24 #include "write-mo.h"
25
26 #include <errno.h>
27 #include <stdbool.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31
32 #if HAVE_SYS_PARAM_H
33 # include <sys/param.h>
34 #endif
35
36 /* These two include files describe the binary .mo format.  */
37 #include "gmo.h"
38 #include "hash-string.h"
39
40 #include "byteswap.h"
41 #include "error.h"
42 #include "hash.h"
43 #include "message.h"
44 #include "format.h"
45 #include "xsize.h"
46 #include "xalloc.h"
47 #include "xmalloca.h"
48 #include "binary-io.h"
49 #include "fwriteerror.h"
50 #include "gettext.h"
51
52 #define _(str) gettext (str)
53
54 #define freea(p) /* nothing */
55
56 /* Usually defined in <sys/param.h>.  */
57 #ifndef roundup
58 # if defined __GNUC__ && __GNUC__ >= 2
59 #  define roundup(x, y) ({typeof(x) _x = (x); typeof(y) _y = (y); \
60                           ((_x + _y - 1) / _y) * _y; })
61 # else
62 #  define roundup(x, y) ((((x)+((y)-1))/(y))*(y))
63 # endif /* GNU CC2  */
64 #endif /* roundup  */
65
66
67 /* Alignment of strings in resulting .mo file.  */
68 size_t alignment;
69
70 /* True if writing a .mo file in opposite endianness than the host.  */
71 bool byteswap;
72
73 /* True if no hash table in .mo is wanted.  */
74 bool no_hash_table;
75
76
77 /* Destructively changes the byte order of a 32-bit value in memory.  */
78 #define BSWAP32(x) (x) = bswap_32 (x)
79
80
81 /* Indices into the strings contained in 'struct pre_message' and
82    'struct pre_sysdep_message'.  */
83 enum
84 {
85   M_ID = 0,     /* msgid - the original string */
86   M_STR = 1     /* msgstr - the translated string */
87 };
88
89 /* An intermediate data structure representing a 'struct string_desc'.  */
90 struct pre_string
91 {
92   size_t length;
93   const char *pointer;
94 };
95
96 /* An intermediate data structure representing a message.  */
97 struct pre_message
98 {
99   struct pre_string str[2];
100   const char *id_plural;
101   size_t id_plural_len;
102 };
103
104 static int
105 compare_id (const void *pval1, const void *pval2)
106 {
107   return strcmp (((struct pre_message *) pval1)->str[M_ID].pointer,
108                  ((struct pre_message *) pval2)->str[M_ID].pointer);
109 }
110
111
112 /* An intermediate data structure representing a 'struct sysdep_segment'.  */
113 struct pre_sysdep_segment
114 {
115   size_t length;
116   const char *pointer;
117 };
118
119 /* An intermediate data structure representing a 'struct segment_pair'.  */
120 struct pre_segment_pair
121 {
122   size_t segsize;
123   const char *segptr;
124   size_t sysdepref;
125 };
126
127 /* An intermediate data structure representing a 'struct sysdep_string'.  */
128 struct pre_sysdep_string
129 {
130   unsigned int segmentcount;
131   struct pre_segment_pair segments[1];
132 };
133
134 /* An intermediate data structure representing a message with system dependent
135    strings.  */
136 struct pre_sysdep_message
137 {
138   struct pre_sysdep_string *str[2];
139   const char *id_plural;
140   size_t id_plural_len;
141 };
142
143 /* Write the message list to the given open file.  */
144 static void
145 write_table (FILE *output_file, message_list_ty *mlp)
146 {
147   char **msgctid_arr;
148   size_t nstrings;
149   struct pre_message *msg_arr;
150   size_t n_sysdep_strings;
151   struct pre_sysdep_message *sysdep_msg_arr;
152   size_t n_sysdep_segments;
153   struct pre_sysdep_segment *sysdep_segments;
154   bool have_outdigits;
155   int major_revision;
156   int minor_revision;
157   bool omit_hash_table;
158   nls_uint32 hash_tab_size;
159   struct mo_file_header header; /* Header of the .mo file to be written.  */
160   size_t header_size;
161   size_t offset;
162   struct string_desc *orig_tab;
163   struct string_desc *trans_tab;
164   size_t sysdep_tab_offset = 0;
165   size_t end_offset;
166   char *null;
167   size_t j, m;
168
169   /* First pass: Move the static string pairs into an array, for sorting,
170      and at the same time, compute the segments of the system dependent
171      strings.  */
172   msgctid_arr = XNMALLOC (mlp->nitems, char *);
173   nstrings = 0;
174   msg_arr = XNMALLOC (mlp->nitems, struct pre_message);
175   n_sysdep_strings = 0;
176   sysdep_msg_arr = XNMALLOC (mlp->nitems, struct pre_sysdep_message);
177   n_sysdep_segments = 0;
178   sysdep_segments = NULL;
179   have_outdigits = false;
180   for (j = 0; j < mlp->nitems; j++)
181     {
182       message_ty *mp = mlp->item[j];
183       size_t msgctlen;
184       char *msgctid;
185       struct interval *intervals[2];
186       size_t nintervals[2];
187
188       /* Concatenate mp->msgctxt and mp->msgid into msgctid.  */
189       msgctlen = (mp->msgctxt != NULL ? strlen (mp->msgctxt) + 1 : 0);
190       msgctid = XNMALLOC (msgctlen + strlen (mp->msgid) + 1, char);
191       if (mp->msgctxt != NULL)
192         {
193           memcpy (msgctid, mp->msgctxt, msgctlen - 1);
194           msgctid[msgctlen - 1] = MSGCTXT_SEPARATOR;
195         }
196       strcpy (msgctid + msgctlen, mp->msgid);
197       msgctid_arr[j] = msgctid;
198
199       intervals[M_ID] = NULL;
200       nintervals[M_ID] = 0;
201       intervals[M_STR] = NULL;
202       nintervals[M_STR] = 0;
203
204       /* Test if mp contains system dependent strings and thus
205          requires the use of the .mo file minor revision 1.  */
206       if (possible_format_p (mp->is_format[format_c])
207           || possible_format_p (mp->is_format[format_objc]))
208         {
209           /* Check whether msgid or msgstr contain ISO C 99 <inttypes.h>
210              format string directives.  No need to check msgid_plural, because
211              it is not accessed by the [n]gettext() function family.  */
212           const char *p_end;
213           const char *p;
214
215           get_sysdep_c_format_directives (mp->msgid, false,
216                                           &intervals[M_ID], &nintervals[M_ID]);
217           if (msgctlen > 0)
218             {
219               struct interval *id_intervals = intervals[M_ID];
220               size_t id_nintervals = nintervals[M_ID];
221
222               if (id_nintervals > 0)
223                 {
224                   unsigned int i;
225
226                   for (i = 0; i < id_nintervals; i++)
227                     {
228                       id_intervals[i].startpos += msgctlen;
229                       id_intervals[i].endpos += msgctlen;
230                     }
231                 }
232             }
233
234           p_end = mp->msgstr + mp->msgstr_len;
235           for (p = mp->msgstr; p < p_end; p += strlen (p) + 1)
236             {
237               struct interval *part_intervals;
238               size_t part_nintervals;
239
240               get_sysdep_c_format_directives (p, true,
241                                               &part_intervals,
242                                               &part_nintervals);
243               if (part_nintervals > 0)
244                 {
245                   size_t d = p - mp->msgstr;
246                   unsigned int i;
247
248                   intervals[M_STR] =
249                     (struct interval *)
250                     xrealloc (intervals[M_STR],
251                               (nintervals[M_STR] + part_nintervals)
252                               * sizeof (struct interval));
253                   for (i = 0; i < part_nintervals; i++)
254                     {
255                       intervals[M_STR][nintervals[M_STR] + i].startpos =
256                         d + part_intervals[i].startpos;
257                       intervals[M_STR][nintervals[M_STR] + i].endpos =
258                         d + part_intervals[i].endpos;
259                     }
260                   nintervals[M_STR] += part_nintervals;
261                 }
262             }
263         }
264
265       if (nintervals[M_ID] > 0 || nintervals[M_STR] > 0)
266         {
267           /* System dependent string pair.  */
268           for (m = 0; m < 2; m++)
269             {
270               struct pre_sysdep_string *pre =
271                 (struct pre_sysdep_string *)
272                 xmalloc (xsum (sizeof (struct pre_sysdep_string),
273                                xtimes (nintervals[m],
274                                        sizeof (struct pre_segment_pair))));
275               const char *str;
276               size_t str_len;
277               size_t lastpos;
278               unsigned int i;
279
280               if (m == M_ID)
281                 {
282                   str = msgctid; /* concatenation of mp->msgctxt + mp->msgid  */
283                   str_len = strlen (msgctid) + 1;
284                 }
285               else
286                 {
287                   str = mp->msgstr;
288                   str_len = mp->msgstr_len;
289                 }
290
291               lastpos = 0;
292               pre->segmentcount = nintervals[m];
293               for (i = 0; i < nintervals[m]; i++)
294                 {
295                   size_t length;
296                   const char *pointer;
297                   size_t r;
298
299                   pre->segments[i].segptr = str + lastpos;
300                   pre->segments[i].segsize = intervals[m][i].startpos - lastpos;
301
302                   length = intervals[m][i].endpos - intervals[m][i].startpos;
303                   pointer = str + intervals[m][i].startpos;
304                   if (length >= 2
305                       && pointer[0] == '<' && pointer[length - 1] == '>')
306                     {
307                       /* Skip the '<' and '>' markers.  */
308                       length -= 2;
309                       pointer += 1;
310                     }
311
312                   for (r = 0; r < n_sysdep_segments; r++)
313                     if (sysdep_segments[r].length == length
314                         && memcmp (sysdep_segments[r].pointer, pointer, length)
315                            == 0)
316                       break;
317                   if (r == n_sysdep_segments)
318                     {
319                       n_sysdep_segments++;
320                       sysdep_segments =
321                         (struct pre_sysdep_segment *)
322                         xrealloc (sysdep_segments,
323                                   n_sysdep_segments
324                                   * sizeof (struct pre_sysdep_segment));
325                       sysdep_segments[r].length = length;
326                       sysdep_segments[r].pointer = pointer;
327                     }
328
329                   pre->segments[i].sysdepref = r;
330
331                   if (length == 1 && *pointer == 'I')
332                     have_outdigits = true;
333
334                   lastpos = intervals[m][i].endpos;
335                 }
336               pre->segments[i].segptr = str + lastpos;
337               pre->segments[i].segsize = str_len - lastpos;
338               pre->segments[i].sysdepref = SEGMENTS_END;
339
340               sysdep_msg_arr[n_sysdep_strings].str[m] = pre;
341             }
342
343           sysdep_msg_arr[n_sysdep_strings].id_plural = mp->msgid_plural;
344           sysdep_msg_arr[n_sysdep_strings].id_plural_len =
345             (mp->msgid_plural != NULL ? strlen (mp->msgid_plural) + 1 : 0);
346           n_sysdep_strings++;
347         }
348       else
349         {
350           /* Static string pair.  */
351           msg_arr[nstrings].str[M_ID].pointer = msgctid;
352           msg_arr[nstrings].str[M_ID].length = strlen (msgctid) + 1;
353           msg_arr[nstrings].str[M_STR].pointer = mp->msgstr;
354           msg_arr[nstrings].str[M_STR].length = mp->msgstr_len;
355           msg_arr[nstrings].id_plural = mp->msgid_plural;
356           msg_arr[nstrings].id_plural_len =
357             (mp->msgid_plural != NULL ? strlen (mp->msgid_plural) + 1 : 0);
358           nstrings++;
359         }
360
361       for (m = 0; m < 2; m++)
362         if (intervals[m] != NULL)
363           free (intervals[m]);
364     }
365
366   /* Sort the table according to original string.  */
367   if (nstrings > 0)
368     qsort (msg_arr, nstrings, sizeof (struct pre_message), compare_id);
369
370   /* We need major revision 1 if there are system dependent strings that use
371      "I" because older versions of gettext() crash when this occurs in a .mo
372      file.  Otherwise use major revision 0.  */
373   major_revision =
374     (have_outdigits ? MO_REVISION_NUMBER_WITH_SYSDEP_I : MO_REVISION_NUMBER);
375
376   /* We need minor revision 1 if there are system dependent strings.
377      Otherwise we choose minor revision 0 because it's supported by older
378      versions of libintl and revision 1 isn't.  */
379   minor_revision = (n_sysdep_strings > 0 ? 1 : 0);
380
381   /* In minor revision >= 1, the hash table is obligatory.  */
382   omit_hash_table = (no_hash_table && minor_revision == 0);
383
384   /* This should be explained:
385      Each string has an associate hashing value V, computed by a fixed
386      function.  To locate the string we use open addressing with double
387      hashing.  The first index will be V % M, where M is the size of the
388      hashing table.  If no entry is found, iterating with a second,
389      independent hashing function takes place.  This second value will
390      be 1 + V % (M - 2).
391      The approximate number of probes will be
392
393        for unsuccessful search:  (1 - N / M) ^ -1
394        for successful search:    - (N / M) ^ -1 * ln (1 - N / M)
395
396      where N is the number of keys.
397
398      If we now choose M to be the next prime bigger than 4 / 3 * N,
399      we get the values
400                          4   and   1.85  resp.
401      Because unsuccessful searches are unlikely this is a good value.
402      Formulas: [Knuth, The Art of Computer Programming, Volume 3,
403                 Sorting and Searching, 1973, Addison Wesley]  */
404   if (!omit_hash_table)
405     {
406       hash_tab_size = next_prime ((mlp->nitems * 4) / 3);
407       /* Ensure M > 2.  */
408       if (hash_tab_size <= 2)
409         hash_tab_size = 3;
410     }
411   else
412     hash_tab_size = 0;
413
414
415   /* Second pass: Fill the structure describing the header.  At the same time,
416      compute the sizes and offsets of the non-string parts of the file.  */
417
418   /* Magic number.  */
419   header.magic = _MAGIC;
420   /* Revision number of file format.  */
421   header.revision = (major_revision << 16) + minor_revision;
422
423   header_size =
424     (minor_revision == 0
425      ? offsetof (struct mo_file_header, n_sysdep_segments)
426      : sizeof (struct mo_file_header));
427   offset = header_size;
428
429   /* Number of static string pairs.  */
430   header.nstrings = nstrings;
431
432   /* Offset of table for original string offsets.  */
433   header.orig_tab_offset = offset;
434   offset += nstrings * sizeof (struct string_desc);
435   orig_tab = XNMALLOC (nstrings, struct string_desc);
436
437   /* Offset of table for translated string offsets.  */
438   header.trans_tab_offset = offset;
439   offset += nstrings * sizeof (struct string_desc);
440   trans_tab = XNMALLOC (nstrings, struct string_desc);
441
442   /* Size of hash table.  */
443   header.hash_tab_size = hash_tab_size;
444   /* Offset of hash table.  */
445   header.hash_tab_offset = offset;
446   offset += hash_tab_size * sizeof (nls_uint32);
447
448   if (minor_revision >= 1)
449     {
450       /* Size of table describing system dependent segments.  */
451       header.n_sysdep_segments = n_sysdep_segments;
452       /* Offset of table describing system dependent segments.  */
453       header.sysdep_segments_offset = offset;
454       offset += n_sysdep_segments * sizeof (struct sysdep_segment);
455
456       /* Number of system dependent string pairs.  */
457       header.n_sysdep_strings = n_sysdep_strings;
458
459       /* Offset of table for original sysdep string offsets.  */
460       header.orig_sysdep_tab_offset = offset;
461       offset += n_sysdep_strings * sizeof (nls_uint32);
462
463       /* Offset of table for translated sysdep string offsets.  */
464       header.trans_sysdep_tab_offset = offset;
465       offset += n_sysdep_strings * sizeof (nls_uint32);
466
467       /* System dependent string descriptors.  */
468       sysdep_tab_offset = offset;
469       for (m = 0; m < 2; m++)
470         for (j = 0; j < n_sysdep_strings; j++)
471           offset += sizeof (struct sysdep_string)
472                     + sysdep_msg_arr[j].str[m]->segmentcount
473                       * sizeof (struct segment_pair);
474     }
475
476   end_offset = offset;
477
478
479   /* Third pass: Write the non-string parts of the file.  At the same time,
480      compute the offsets of each string, including the proper alignment.  */
481
482   /* Write the header out.  */
483   if (byteswap)
484     {
485       BSWAP32 (header.magic);
486       BSWAP32 (header.revision);
487       BSWAP32 (header.nstrings);
488       BSWAP32 (header.orig_tab_offset);
489       BSWAP32 (header.trans_tab_offset);
490       BSWAP32 (header.hash_tab_size);
491       BSWAP32 (header.hash_tab_offset);
492       if (minor_revision >= 1)
493         {
494           BSWAP32 (header.n_sysdep_segments);
495           BSWAP32 (header.sysdep_segments_offset);
496           BSWAP32 (header.n_sysdep_strings);
497           BSWAP32 (header.orig_sysdep_tab_offset);
498           BSWAP32 (header.trans_sysdep_tab_offset);
499         }
500     }
501   fwrite (&header, header_size, 1, output_file);
502
503   /* Table for original string offsets.  */
504   /* Here output_file is at position header.orig_tab_offset.  */
505
506   for (j = 0; j < nstrings; j++)
507     {
508       offset = roundup (offset, alignment);
509       orig_tab[j].length =
510         msg_arr[j].str[M_ID].length + msg_arr[j].id_plural_len;
511       orig_tab[j].offset = offset;
512       offset += orig_tab[j].length;
513       /* Subtract 1 because of the terminating NUL.  */
514       orig_tab[j].length--;
515     }
516   if (byteswap)
517     for (j = 0; j < nstrings; j++)
518       {
519         BSWAP32 (orig_tab[j].length);
520         BSWAP32 (orig_tab[j].offset);
521       }
522   fwrite (orig_tab, nstrings * sizeof (struct string_desc), 1, output_file);
523
524   /* Table for translated string offsets.  */
525   /* Here output_file is at position header.trans_tab_offset.  */
526
527   for (j = 0; j < nstrings; j++)
528     {
529       offset = roundup (offset, alignment);
530       trans_tab[j].length = msg_arr[j].str[M_STR].length;
531       trans_tab[j].offset = offset;
532       offset += trans_tab[j].length;
533       /* Subtract 1 because of the terminating NUL.  */
534       trans_tab[j].length--;
535     }
536   if (byteswap)
537     for (j = 0; j < nstrings; j++)
538       {
539         BSWAP32 (trans_tab[j].length);
540         BSWAP32 (trans_tab[j].offset);
541       }
542   fwrite (trans_tab, nstrings * sizeof (struct string_desc), 1, output_file);
543
544   /* Skip this part when no hash table is needed.  */
545   if (!omit_hash_table)
546     {
547       nls_uint32 *hash_tab;
548       unsigned int j;
549
550       /* Here output_file is at position header.hash_tab_offset.  */
551
552       /* Allocate room for the hashing table to be written out.  */
553       hash_tab = XNMALLOC (hash_tab_size, nls_uint32);
554       memset (hash_tab, '\0', hash_tab_size * sizeof (nls_uint32));
555
556       /* Insert all value in the hash table, following the algorithm described
557          above.  */
558       for (j = 0; j < nstrings; j++)
559         {
560           nls_uint32 hash_val = hash_string (msg_arr[j].str[M_ID].pointer);
561           nls_uint32 idx = hash_val % hash_tab_size;
562
563           if (hash_tab[idx] != 0)
564             {
565               /* We need the second hashing function.  */
566               nls_uint32 incr = 1 + (hash_val % (hash_tab_size - 2));
567
568               do
569                 if (idx >= hash_tab_size - incr)
570                   idx -= hash_tab_size - incr;
571                 else
572                   idx += incr;
573               while (hash_tab[idx] != 0);
574             }
575
576           hash_tab[idx] = j + 1;
577         }
578
579       /* Write the hash table out.  */
580       if (byteswap)
581         for (j = 0; j < hash_tab_size; j++)
582           BSWAP32 (hash_tab[j]);
583       fwrite (hash_tab, hash_tab_size * sizeof (nls_uint32), 1, output_file);
584
585       free (hash_tab);
586     }
587
588   if (minor_revision >= 1)
589     {
590       struct sysdep_segment *sysdep_segments_tab;
591       nls_uint32 *sysdep_tab;
592       size_t stoffset;
593       unsigned int i;
594
595       /* Here output_file is at position header.sysdep_segments_offset.  */
596
597       sysdep_segments_tab =
598         XNMALLOC (n_sysdep_segments, struct sysdep_segment);
599       for (i = 0; i < n_sysdep_segments; i++)
600         {
601           offset = roundup (offset, alignment);
602           /* The "+ 1" accounts for the trailing NUL byte.  */
603           sysdep_segments_tab[i].length = sysdep_segments[i].length + 1;
604           sysdep_segments_tab[i].offset = offset;
605           offset += sysdep_segments_tab[i].length;
606         }
607
608       if (byteswap)
609         for (i = 0; i < n_sysdep_segments; i++)
610           {
611             BSWAP32 (sysdep_segments_tab[i].length);
612             BSWAP32 (sysdep_segments_tab[i].offset);
613           }
614       fwrite (sysdep_segments_tab,
615               n_sysdep_segments * sizeof (struct sysdep_segment), 1,
616               output_file);
617
618       free (sysdep_segments_tab);
619
620       sysdep_tab = XNMALLOC (n_sysdep_strings, nls_uint32);
621       stoffset = sysdep_tab_offset;
622
623       for (m = 0; m < 2; m++)
624         {
625           /* Here output_file is at position
626              m == M_ID  -> header.orig_sysdep_tab_offset,
627              m == M_STR -> header.trans_sysdep_tab_offset.  */
628
629           for (j = 0; j < n_sysdep_strings; j++)
630             {
631               sysdep_tab[j] = stoffset;
632               stoffset += sizeof (struct sysdep_string)
633                           + sysdep_msg_arr[j].str[m]->segmentcount
634                             * sizeof (struct segment_pair);
635             }
636           /* Write the table for original/translated sysdep string offsets.  */
637           if (byteswap)
638             for (j = 0; j < n_sysdep_strings; j++)
639               BSWAP32 (sysdep_tab[j]);
640           fwrite (sysdep_tab, n_sysdep_strings * sizeof (nls_uint32), 1,
641                   output_file);
642         }
643
644       free (sysdep_tab);
645
646       /* Here output_file is at position sysdep_tab_offset.  */
647
648       for (m = 0; m < 2; m++)
649         for (j = 0; j < n_sysdep_strings; j++)
650           {
651             struct pre_sysdep_message *msg = &sysdep_msg_arr[j];
652             struct pre_sysdep_string *pre = msg->str[m];
653             struct sysdep_string *str =
654               (struct sysdep_string *)
655               xmalloca (sizeof (struct sysdep_string)
656                         + pre->segmentcount * sizeof (struct segment_pair));
657             unsigned int i;
658
659             offset = roundup (offset, alignment);
660             str->offset = offset;
661             for (i = 0; i <= pre->segmentcount; i++)
662               {
663                 str->segments[i].segsize = pre->segments[i].segsize;
664                 str->segments[i].sysdepref = pre->segments[i].sysdepref;
665                 offset += str->segments[i].segsize;
666               }
667             if (m == M_ID && msg->id_plural_len > 0)
668               {
669                 str->segments[pre->segmentcount].segsize += msg->id_plural_len;
670                 offset += msg->id_plural_len;
671               }
672             if (byteswap)
673               {
674                 BSWAP32 (str->offset);
675                 for (i = 0; i <= pre->segmentcount; i++)
676                   {
677                     BSWAP32 (str->segments[i].segsize);
678                     BSWAP32 (str->segments[i].sysdepref);
679                   }
680               }
681             fwrite (str,
682                     sizeof (struct sysdep_string)
683                     + pre->segmentcount * sizeof (struct segment_pair),
684                     1, output_file);
685
686             freea (str);
687           }
688     }
689
690   /* Here output_file is at position end_offset.  */
691
692   free (trans_tab);
693   free (orig_tab);
694
695
696   /* Fourth pass: Write the strings.  */
697
698   offset = end_offset;
699
700   /* A few zero bytes for padding.  */
701   null = (char *) alloca (alignment);
702   memset (null, '\0', alignment);
703
704   /* Now write the original strings.  */
705   for (j = 0; j < nstrings; j++)
706     {
707       fwrite (null, roundup (offset, alignment) - offset, 1, output_file);
708       offset = roundup (offset, alignment);
709
710       fwrite (msg_arr[j].str[M_ID].pointer, msg_arr[j].str[M_ID].length, 1,
711               output_file);
712       if (msg_arr[j].id_plural_len > 0)
713         fwrite (msg_arr[j].id_plural, msg_arr[j].id_plural_len, 1,
714                 output_file);
715       offset += msg_arr[j].str[M_ID].length + msg_arr[j].id_plural_len;
716     }
717
718   /* Now write the translated strings.  */
719   for (j = 0; j < nstrings; j++)
720     {
721       fwrite (null, roundup (offset, alignment) - offset, 1, output_file);
722       offset = roundup (offset, alignment);
723
724       fwrite (msg_arr[j].str[M_STR].pointer, msg_arr[j].str[M_STR].length, 1,
725               output_file);
726       offset += msg_arr[j].str[M_STR].length;
727     }
728
729   if (minor_revision >= 1)
730     {
731       unsigned int i;
732
733       for (i = 0; i < n_sysdep_segments; i++)
734         {
735           fwrite (null, roundup (offset, alignment) - offset, 1, output_file);
736           offset = roundup (offset, alignment);
737
738           fwrite (sysdep_segments[i].pointer, sysdep_segments[i].length, 1,
739                   output_file);
740           fwrite (null, 1, 1, output_file);
741           offset += sysdep_segments[i].length + 1;
742         }
743
744       for (m = 0; m < 2; m++)
745         for (j = 0; j < n_sysdep_strings; j++)
746           {
747             struct pre_sysdep_message *msg = &sysdep_msg_arr[j];
748             struct pre_sysdep_string *pre = msg->str[m];
749
750             fwrite (null, roundup (offset, alignment) - offset, 1,
751                     output_file);
752             offset = roundup (offset, alignment);
753
754             for (i = 0; i <= pre->segmentcount; i++)
755               {
756                 fwrite (pre->segments[i].segptr, pre->segments[i].segsize, 1,
757                         output_file);
758                 offset += pre->segments[i].segsize;
759               }
760             if (m == M_ID && msg->id_plural_len > 0)
761               {
762                 fwrite (msg->id_plural, msg->id_plural_len, 1, output_file);
763                 offset += msg->id_plural_len;
764               }
765
766             free (pre);
767           }
768     }
769
770   freea (null);
771   for (j = 0; j < mlp->nitems; j++)
772     free (msgctid_arr[j]);
773   free (sysdep_msg_arr);
774   free (msg_arr);
775   free (msgctid_arr);
776 }
777
778
779 int
780 msgdomain_write_mo (message_list_ty *mlp,
781                     const char *domain_name,
782                     const char *file_name)
783 {
784   FILE *output_file;
785
786   /* If no entry for this domain don't even create the file.  */
787   if (mlp->nitems != 0)
788     {
789       if (strcmp (domain_name, "-") == 0)
790         {
791           output_file = stdout;
792           SET_BINARY (fileno (output_file));
793         }
794       else
795         {
796           output_file = fopen (file_name, "wb");
797           if (output_file == NULL)
798             {
799               error (0, errno, _("error while opening \"%s\" for writing"),
800                      file_name);
801               return 1;
802             }
803         }
804
805       if (output_file != NULL)
806         {
807           write_table (output_file, mlp);
808
809           /* Make sure nothing went wrong.  */
810           if (fwriteerror (output_file))
811             error (EXIT_FAILURE, errno, _("error while writing \"%s\" file"),
812                    file_name);
813         }
814     }
815
816   return 0;
817 }