• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /netgear-WNDR4500v2-V1.0.0.60_1.0.38/ap/gpl/timemachine/gettext-0.17/gettext-tools/src/
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.  */
68size_t alignment;
69
70/* True if writing a .mo file in opposite endianness than the host.  */
71bool byteswap;
72
73/* True if no hash table in .mo is wanted.  */
74bool 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'.  */
83enum
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'.  */
90struct pre_string
91{
92  size_t length;
93  const char *pointer;
94};
95
96/* An intermediate data structure representing a message.  */
97struct pre_message
98{
99  struct pre_string str[2];
100  const char *id_plural;
101  size_t id_plural_len;
102};
103
104static int
105compare_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'.  */
113struct pre_sysdep_segment
114{
115  size_t length;
116  const char *pointer;
117};
118
119/* An intermediate data structure representing a 'struct segment_pair'.  */
120struct 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'.  */
128struct 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.  */
136struct 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.  */
144static void
145write_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
779int
780msgdomain_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}
818