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