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