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