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