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