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 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 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 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