1 /* Writing Qt .qm files.
2    Copyright (C) 2003, 2005 Free Software Foundation, Inc.
3    Written by Bruno Haible <bruno@clisp.org>, 2003.
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 
23 /* Specification.  */
24 #include "write-qt.h"
25 
26 #include <assert.h>
27 #include <errno.h>
28 #include <stdbool.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 
33 #include "error.h"
34 #include "xerror.h"
35 #include "message.h"
36 #include "po-charset.h"
37 #include "msgl-iconv.h"
38 #include "hash-string.h"
39 #include "utf8-ucs4.h"
40 #include "xalloc.h"
41 #include "obstack.h"
42 #include "hash.h"
43 #include "binary-io.h"
44 #include "fwriteerror.h"
45 #include "exit.h"
46 #include "gettext.h"
47 
48 #define _(str) gettext (str)
49 
50 /* Qt .qm files are read by the QTranslator::load() function and written
51    by the Qt QTranslator::save() function.
52 
53    The Qt tool 'msg2qm' uses the latter function and can convert PO files
54    to .qm files. But since 'msg2qm' is marked as an "old" tool in Qt 3.0.5's
55    i18n.html documentation and therefore likely to disappear, we provide the
56    same functionality here.
57 
58    The format of .qm files, as reverse engineered from the functions
59      QTranslator::save(const QString& filename, SaveMode mode)
60      QTranslator::squeeze(SaveMode mode)
61      QTranslatorMessage::write(QDataStream& stream, bool strip, Prefix prefix)
62      elfHash(const char* name)
63    in qt-3.0.5, is as follows:
64 
65      It's a binary data format. Elements are u8 (byte), u16, u32. They are
66      written in big-endian order.
67 
68      The file starts with a magic string of 16 bytes:
69        3C B8 64 18 CA EF 9C 95 CD 21 1C BF 60 A1 BD DD
70 
71      Then come three sections. Each of the three sections is optional. Each
72      has this structure:
73        struct {
74          u8 section_type; // 0x42 = hashes, 0x69 = messages, 0x2f = contexts
75          u32 length; // number of bytes of the data
76          u8 data[length];
77        };
78 
79      In the first section, the hashes section, the data has the following
80      structure:
81        It's a sorted array of
82          struct {
83            u32 hashcode; // elfHash of the concatenation of msgid and
84                          // disambiguating-comment
85            u32 offset; // offset within the data[] of the messages section
86          };
87        It's sorted in ascending order by hashcode as primary sorting criteria
88        and - when the hashcodes are the same - by offset as secondary criteria.
89 
90      In the second section, the messages section, the data has the following
91      structure:
92        It's a sequence of records, each representing a message, in no
93        particular order. Each record is a sequence of subsections, each
94        introduced by a particular subsection tag. The possible subsection tags
95        are (and they usually occur in this order):
96          - 03: Translation. Followed by the msgstr in UCS-2 or UTF-16 format:
97                struct {
98                  u32 length;
99                  u16 chars[length/2];
100                };
101          - 08: Disambiguating-comment. Followed by the NUL-terminated,
102                ISO-8859-1 encoded, disambiguating-comment string:
103                struct {
104                  u32 length;    // number of bytes including the NUL at the end
105                  u8 chars[length];
106                };
107          - 06: SourceText, i.e. msgid. Followed by the NUL-terminated,
108                ISO-8859-1 encoded, msgid:
109                struct {
110                  u32 length;    // number of bytes including the NUL at the end
111                  u8 chars[length];
112                };
113          - 02: SourceText16, i.e. msgid. Encoded as UCS-2, but must actually
114                be ISO-8859-1.
115                struct {
116                  u32 length;
117                  u16 chars[length/2];
118                };
119                This subsection tag is obsoleted by SourceText.
120          - 07: Context. Followed by the NUL-terminated, ISO-8859-1 encoded,
121                context string (usually a C++ class name or empty):
122                struct {
123                  u32 length;    // number of bytes including the NUL at the end
124                  u8 chars[length];
125                };
126          - 04: Context16. Encoded as UCS-2, but must actually be ISO-8859-1.
127                struct {
128                  u32 length;
129                  u16 chars[length/2];
130                };
131                This subsection tag is obsoleted by Context.
132          - 05: Hash. Followed by
133                struct {
134                  u32 hashcode; // elfHash of the concatenation of msgid and
135                                // disambiguating-comment
136                };
137          - 01: End. Designates the end of the record. No further data.
138        Usually the following subsections are written, but some of them are
139        optional:
140          - 03: Translation.
141          - 08: Disambiguating-comment (optional).
142          - 06: SourceText (optional).
143          - 07: Context (optional).
144          - 05: Hash.
145          - 01: End.
146        A subsection can be omitted if the value to be output is the same as
147        for the previous record.
148 
149      The third section, the contexts section, contains the set of all occurring
150      context strings. This section is optional; it is used to speed up the
151      search. The data is a hash table with the following structure:
152        struct {
153          u16 table_size;
154          u16 buckets[table_size];
155          u8 pool[...];
156        };
157      pool[...] contains:
158        u16 zero;
159        for i = 0, ..., table_size:
160          if there are context strings with elfHash(context)%table_size == i:
161            for all context strings with elfHash(context)%table_size == i:
162              len := min(length(context),255); // truncated to length 255
163              struct {
164                u8 len;
165                u8 chars[len];
166              };
167            struct {
168              u8 zero[1]; // signals the end of this bucket
169              u8 padding[0 or 1]; // padding for even number of bytes
170            };
171      buckets[i] is 0 for an empty bucket, or the offset in pool[] where
172      the context strings for this bucket start, divided by 2.
173      This context section must not be used
174        - if the empty context is used, or
175        - if a context of length > 255 is used, or
176        - if the context pool's size would be > 2^17.
177 
178      The elfHash function is the same as our hash_string function, except that
179      at the end it maps a hash code of 0x00000000 to 0x00000001.
180 
181    When we convert from PO file format, all disambiguating-comments and
182    contexts are empty, and therefore the contexts section can be omitted.  */
183 
184 
185 /* Write a u8 (a single byte) to the output stream.  */
186 static inline void
write_u8(FILE * output_file,unsigned char value)187 write_u8 (FILE *output_file, unsigned char value)
188 {
189   putc (value, output_file);
190 }
191 
192 /* Write a u16 (two bytes) to the output stream.  */
193 static inline void
write_u16(FILE * output_file,unsigned short value)194 write_u16 (FILE *output_file, unsigned short value)
195 {
196   unsigned char data[2];
197 
198   data[0] = (value >> 8) & 0xff;
199   data[1] = value & 0xff;
200 
201   fwrite (data, 2, 1, output_file);
202 }
203 
204 /* Write a u32 (four bytes) to the output stream.  */
205 static inline void
write_u32(FILE * output_file,unsigned int value)206 write_u32 (FILE *output_file, unsigned int value)
207 {
208   unsigned char data[4];
209 
210   data[0] = (value >> 24) & 0xff;
211   data[1] = (value >> 16) & 0xff;
212   data[2] = (value >> 8) & 0xff;
213   data[3] = value & 0xff;
214 
215   fwrite (data, 4, 1, output_file);
216 }
217 
218 
219 #define obstack_chunk_alloc xmalloc
220 #define obstack_chunk_free free
221 
222 /* Add a u8 (a single byte) to an obstack.  */
223 static void
append_u8(struct obstack * mempool,unsigned char value)224 append_u8 (struct obstack *mempool, unsigned char value)
225 {
226   unsigned char data[1];
227 
228   data[0] = value;
229 
230   obstack_grow (mempool, data, 1);
231 }
232 
233 /* Add a u16 (two bytes) to an obstack.  */
234 static void
append_u16(struct obstack * mempool,unsigned short value)235 append_u16 (struct obstack *mempool, unsigned short value)
236 {
237   unsigned char data[2];
238 
239   data[0] = (value >> 8) & 0xff;
240   data[1] = value & 0xff;
241 
242   obstack_grow (mempool, data, 2);
243 }
244 
245 /* Add a u32 (four bytes) to an obstack.  */
246 static void
append_u32(struct obstack * mempool,unsigned int value)247 append_u32 (struct obstack *mempool, unsigned int value)
248 {
249   unsigned char data[4];
250 
251   data[0] = (value >> 24) & 0xff;
252   data[1] = (value >> 16) & 0xff;
253   data[2] = (value >> 8) & 0xff;
254   data[3] = value & 0xff;
255 
256   obstack_grow (mempool, data, 4);
257 }
258 
259 /* Add an ISO-8859-1 encoded string to an obstack.  */
260 static void
append_base_string(struct obstack * mempool,const char * string)261 append_base_string (struct obstack *mempool, const char *string)
262 {
263   size_t length = strlen (string) + 1;
264   append_u32 (mempool, length);
265   obstack_grow (mempool, string, length);
266 }
267 
268 /* Add an UTF-16 encoded string to an obstack.  */
269 static void
append_unicode_string(struct obstack * mempool,const unsigned short * string,size_t length)270 append_unicode_string (struct obstack *mempool, const unsigned short *string,
271 		       size_t length)
272 {
273   append_u32 (mempool, length * 2);
274   for (; length > 0; string++, length--)
275     append_u16 (mempool, *string);
276 }
277 
278 /* Retrieve a 4-byte integer from memory.  */
279 static inline unsigned int
peek_u32(const unsigned char * p)280 peek_u32 (const unsigned char *p)
281 {
282   return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
283 }
284 
285 /* Convert an UTF-8 string to ISO-8859-1, without error checking.  */
286 static char *
conv_to_iso_8859_1(const char * string)287 conv_to_iso_8859_1 (const char *string)
288 {
289   size_t length = strlen (string);
290   const char *str = string;
291   const char *str_limit = string + length;
292   /* Conversion to ISO-8859-1 can only reduce the number of bytes.  */
293   char *result = (char *) xmalloc (length + 1);
294   char *q = result;
295 
296   while (str < str_limit)
297     {
298       unsigned int uc;
299       str += u8_mbtouc (&uc, (const unsigned char *) str, str_limit - str);
300       /* It has already been verified that the string fits in ISO-8859-1.  */
301       if (!(uc < 0x100))
302 	abort ();
303       /* Store as ISO-8859-1.  */
304       *q++ = (unsigned char) uc;
305     }
306   *q = '\0';
307   assert (q - result <= length);
308 
309   return result;
310 }
311 
312 /* Convert an UTF-8 string to UTF-16, returning its size (number of UTF-16
313    codepoints) in *SIZEP.  */
314 static unsigned short *
conv_to_utf16(const char * string,size_t * sizep)315 conv_to_utf16 (const char *string, size_t *sizep)
316 {
317   size_t length = strlen (string);
318   const char *str = string;
319   const char *str_limit = string + length;
320   /* Conversion to UTF-16 can at most double the number of bytes.  */
321   unsigned short *result = (unsigned short *) xmalloc (2 * length);
322   unsigned short *q = result;
323 
324   while (str < str_limit)
325     {
326       unsigned int uc;
327       str += u8_mbtouc (&uc, (const unsigned char *) str, str_limit - str);
328       if (uc < 0x10000)
329 	/* UCS-2 character.  */
330 	*q++ = (unsigned short) uc;
331       else
332 	{
333 	  /* UTF-16 surrogate.  */
334 	  *q++ = 0xd800 + ((uc - 0x10000) >> 10);
335 	  *q++ = 0xdc00 + ((uc - 0x10000) & 0x3ff);
336 	}
337     }
338   assert (q - result <= 2 * length);
339 
340   *sizep = q - result;
341   return result;
342 }
343 
344 /* Return the Qt hash code of a string.  */
345 static unsigned int
string_hashcode(const char * str)346 string_hashcode (const char *str)
347 {
348   unsigned int h;
349 
350   h = hash_string (str);
351   if (h == 0)
352     h = 1;
353   return h;
354 }
355 
356 /* Compare two entries of the hashes section.  */
357 static int
cmp_hashes(const void * va,const void * vb)358 cmp_hashes (const void *va, const void *vb)
359 {
360   const unsigned char *a = (const unsigned char *) va;
361   const unsigned char *b = (const unsigned char *) vb;
362   unsigned int a_hashcode = peek_u32 (a);
363   unsigned int b_hashcode = peek_u32 (b);
364 
365   if (a_hashcode != b_hashcode)
366     return (a_hashcode >= b_hashcode ? 1 : -1);
367   else
368     {
369       unsigned int a_offset = peek_u32 (a + 4);
370       unsigned int b_offset = peek_u32 (b + 4);
371 
372       if (a_offset != b_offset)
373 	return (a_offset >= b_offset ? 1 : -1);
374       else
375 	return 0;
376     }
377 }
378 
379 
380 /* Write a section to the output stream.  */
381 static void
write_section(FILE * output_file,unsigned char tag,void * data,size_t size)382 write_section (FILE *output_file, unsigned char tag, void *data, size_t size)
383 {
384   /* A section can be omitted if it is empty.  */
385   if (size > 0)
386     {
387       write_u8 (output_file, tag);
388       write_u32 (output_file, size);
389       fwrite (data, size, 1, output_file);
390     }
391 }
392 
393 
394 /* Write an entire .qm file.  */
395 static void
write_qm(FILE * output_file,message_list_ty * mlp)396 write_qm (FILE *output_file, message_list_ty *mlp)
397 {
398   static unsigned char magic[16] =
399     {
400       0x3C, 0xB8, 0x64, 0x18, 0xCA, 0xEF, 0x9C, 0x95,
401       0xCD, 0x21, 0x1C, 0xBF, 0x60, 0xA1, 0xBD, 0xDD
402     };
403   struct obstack hashes_pool;
404   struct obstack messages_pool;
405   size_t j;
406 
407   obstack_init (&hashes_pool);
408   obstack_init (&messages_pool);
409 
410   /* Prepare the hashes section and the messages section.  */
411   for (j = 0; j < mlp->nitems; j++)
412     {
413       message_ty *mp = mlp->item[j];
414 
415       /* No need to emit the header entry, it's not needed at runtime.  */
416       if (!is_header (mp))
417 	{
418 	  char *msgctxt_as_iso_8859_1 =
419 	    conv_to_iso_8859_1 (mp->msgctxt != NULL ? mp->msgctxt : "");
420 	  char *msgid_as_iso_8859_1 = conv_to_iso_8859_1 (mp->msgid);
421 	  size_t msgstr_len;
422 	  unsigned short *msgstr_as_utf16 =
423 	    conv_to_utf16 (mp->msgstr, &msgstr_len);
424 	  unsigned int hashcode = string_hashcode (msgid_as_iso_8859_1);
425 	  unsigned int offset = obstack_object_size (&messages_pool);
426 
427 	  /* Add a record to the hashes section.  */
428 	  append_u32 (&hashes_pool, hashcode);
429 	  append_u32 (&hashes_pool, offset);
430 
431 	  /* Add a record to the messages section.  */
432 
433 	  append_u8 (&messages_pool, 0x03);
434 	  append_unicode_string (&messages_pool, msgstr_as_utf16, msgstr_len);
435 
436 	  append_u8 (&messages_pool, 0x08);
437 	  append_base_string (&messages_pool, "");
438 
439 	  append_u8 (&messages_pool, 0x06);
440 	  append_base_string (&messages_pool, msgid_as_iso_8859_1);
441 
442 	  append_u8 (&messages_pool, 0x07);
443 	  append_base_string (&messages_pool, msgctxt_as_iso_8859_1);
444 
445 	  append_u8 (&messages_pool, 0x05);
446 	  append_u32 (&messages_pool, hashcode);
447 
448 	  append_u8 (&messages_pool, 0x01);
449 
450 	  free (msgstr_as_utf16);
451 	  free (msgid_as_iso_8859_1);
452 	  free (msgctxt_as_iso_8859_1);
453 	}
454     }
455 
456   /* Sort the hashes section.  */
457   {
458     size_t nstrings = obstack_object_size (&hashes_pool) / 8;
459     if (nstrings > 0)
460       qsort (obstack_base (&hashes_pool), nstrings, 8, cmp_hashes);
461   }
462 
463   /* Write the magic number.  */
464   fwrite (magic, sizeof (magic), 1, output_file);
465 
466   /* Write the hashes section.  */
467   write_section (output_file, 0x42, obstack_base (&hashes_pool),
468 		 obstack_object_size (&hashes_pool));
469 
470   /* Write the messages section.  */
471   write_section (output_file, 0x69, obstack_base (&messages_pool),
472 		 obstack_object_size (&messages_pool));
473 
474   /* Decide whether to write a contexts section.  */
475   {
476     bool can_write_contexts = true;
477 
478     for (j = 0; j < mlp->nitems; j++)
479       {
480 	message_ty *mp = mlp->item[j];
481 
482 	if (!is_header (mp))
483 	  if (mp->msgctxt == NULL || mp->msgctxt[0] == '\0'
484 	      || strlen (mp->msgctxt) > 255)
485 	    {
486 	      can_write_contexts = false;
487 	      break;
488 	    }
489       }
490 
491     if (can_write_contexts)
492       {
493 	hash_table all_contexts;
494 	size_t num_contexts;
495 	unsigned long table_size;
496 
497 	/* Collect the contexts, removing duplicates.  */
498 	hash_init (&all_contexts, 10);
499 	for (j = 0; j < mlp->nitems; j++)
500 	  {
501 	    message_ty *mp = mlp->item[j];
502 
503 	    if (!is_header (mp))
504 	      hash_insert_entry (&all_contexts,
505 				 mp->msgctxt, strlen (mp->msgctxt) + 1,
506 				 NULL);
507 	  }
508 
509 	/* Compute the number of different contexts.  */
510 	num_contexts = all_contexts.size;
511 
512 	/* Compute a suitable hash table size.  */
513 	table_size = next_prime (num_contexts * 1.7);
514 	if (table_size >= 0x10000)
515 	  table_size = 65521;
516 
517 	/* Put the contexts into a hash table of size table_size.  */
518 	{
519 	  struct list_cell { const char *context; struct list_cell *next; };
520 	  struct list_cell *list_memory =
521 	    (struct list_cell *)
522 	    xmalloc (table_size * sizeof (struct list_cell));
523 	  struct list_cell *freelist;
524 	  struct bucket { struct list_cell *head; struct list_cell **tail; };
525 	  struct bucket *buckets =
526 	    (struct bucket *) xmalloc (table_size * sizeof (struct bucket));
527 	  size_t i;
528 
529 	  freelist = list_memory;
530 
531 	  for (i = 0; i < table_size; i++)
532 	    {
533 	      buckets[i].head = NULL;
534 	      buckets[i].tail = &buckets[i].head;
535 	    }
536 
537 	  {
538 	    void *iter;
539 	    const void *key;
540 	    size_t keylen;
541 	    void *null;
542 
543 	    iter = NULL;
544 	    while (hash_iterate (&all_contexts, &iter, &key, &keylen, &null)
545 		   == 0)
546 	      {
547 		const char *context = (const char *)key;
548 		i = string_hashcode (context) % table_size;
549 		freelist->context = context;
550 		freelist->next = NULL;
551 		*buckets[i].tail = freelist;
552 		buckets[i].tail = &freelist->next;
553 		freelist++;
554 	      }
555 	  }
556 
557 	  /* Determine the total context pool size.  */
558 	  {
559 	    size_t pool_size;
560 
561 	    pool_size = 2;
562 	    for (i = 0; i < table_size; i++)
563 	      if (buckets[i].head != NULL)
564 		{
565 		  const struct list_cell *p;
566 
567 		  for (p = buckets[i].head; p != NULL; p = p->next)
568 		    pool_size += 1 + strlen (p->context);
569 		  pool_size++;
570 		  if ((pool_size % 2) != 0)
571 		    pool_size++;
572 		}
573 	    if (pool_size <= 0x20000)
574 	      {
575 		/* Prepare the contexts section.  */
576 		struct obstack contexts_pool;
577 		size_t pool_offset;
578 
579 		obstack_init (&contexts_pool);
580 
581 		append_u16 (&contexts_pool, table_size);
582 		pool_offset = 2;
583 		for (i = 0; i < table_size; i++)
584 		  if (buckets[i].head != NULL)
585 		    {
586 		      const struct list_cell *p;
587 
588 		      append_u16 (&contexts_pool, pool_offset / 2);
589 		      for (p = buckets[i].head; p != NULL; p = p->next)
590 			pool_offset += 1 + strlen (p->context);
591 		      pool_offset++;
592 		      if ((pool_offset % 2) != 0)
593 			pool_offset++;
594 		    }
595 		  else
596 		    append_u16 (&contexts_pool, 0);
597 		if (!(pool_offset == pool_size))
598 		  abort ();
599 
600 		append_u16 (&contexts_pool, 0);
601 		pool_offset = 2;
602 		for (i = 0; i < table_size; i++)
603 		  if (buckets[i].head != NULL)
604 		    {
605 		      const struct list_cell *p;
606 
607 		      for (p = buckets[i].head; p != NULL; p = p->next)
608 			{
609 			  append_u8 (&contexts_pool, strlen (p->context));
610 			  obstack_grow (&contexts_pool,
611 					p->context, strlen (p->context));
612 			  pool_offset += 1 + strlen (p->context);
613 			}
614 		      append_u8 (&contexts_pool, 0);
615 		      pool_offset++;
616 		      if ((pool_offset % 2) != 0)
617 			{
618 			  append_u8 (&contexts_pool, 0);
619 			  pool_offset++;
620 			}
621 		    }
622 		if (!(pool_offset == pool_size))
623 		  abort ();
624 
625 		if (!(obstack_object_size (&contexts_pool)
626 		      == 2 + 2 * table_size + pool_size))
627 		  abort ();
628 
629 		/* Write the contexts section.  */
630 		write_section (output_file, 0x2f, obstack_base (&contexts_pool),
631 			       obstack_object_size (&contexts_pool));
632 
633 		obstack_free (&contexts_pool, NULL);
634 	      }
635 	  }
636 
637 	  free (buckets);
638 	  free (list_memory);
639 	}
640 
641 	hash_destroy (&all_contexts);
642       }
643   }
644 
645   obstack_free (&messages_pool, NULL);
646   obstack_free (&hashes_pool, NULL);
647 }
648 
649 
650 int
msgdomain_write_qt(message_list_ty * mlp,const char * canon_encoding,const char * domain_name,const char * file_name)651 msgdomain_write_qt (message_list_ty *mlp, const char *canon_encoding,
652 		    const char *domain_name, const char *file_name)
653 {
654   FILE *output_file;
655 
656   /* If no entry for this domain don't even create the file.  */
657   if (mlp->nitems != 0)
658     {
659       /* Determine whether mlp has plural entries.  */
660       {
661 	bool has_plural;
662 	size_t j;
663 
664 	has_plural = false;
665 	for (j = 0; j < mlp->nitems; j++)
666 	  if (mlp->item[j]->msgid_plural != NULL)
667 	    has_plural = true;
668 	if (has_plural)
669 	  {
670 	    multiline_error (xstrdup (""),
671 			     xstrdup (_("\
672 message catalog has plural form translations\n\
673 but the Qt message catalog format doesn't support plural handling\n")));
674 	    return 1;
675 	  }
676       }
677 
678       /* Convert the messages to Unicode.  */
679       iconv_message_list (mlp, canon_encoding, po_charset_utf8, NULL);
680 
681       /* Determine whether mlp has non-ISO-8859-1 msgctxt entries.  */
682       {
683 	size_t j;
684 
685 	for (j = 0; j < mlp->nitems; j++)
686 	  {
687 	    const char *string = mlp->item[j]->msgctxt;
688 
689 	    if (string != NULL)
690 	      {
691 		/* An UTF-8 encoded string fits in ISO-8859-1 if and only if
692 		   all its bytes are < 0xc4.  */
693 		for (; *string; string++)
694 		  if ((unsigned char) *string >= 0xc4)
695 		    {
696 		      multiline_error (xstrdup (""),
697 				       xstrdup (_("\
698 message catalog has msgctxt strings containing characters outside ISO-8859-1\n\
699 but the Qt message catalog format supports Unicode only in the translated\n\
700 strings, not in the context strings\n")));
701 		      return 1;
702 		    }
703 	      }
704 	  }
705       }
706 
707       /* Determine whether mlp has non-ISO-8859-1 msgid entries.  */
708       {
709 	size_t j;
710 
711 	for (j = 0; j < mlp->nitems; j++)
712 	  {
713 	    const char *string = mlp->item[j]->msgid;
714 
715 	    /* An UTF-8 encoded string fits in ISO-8859-1 if and only if all
716 	       its bytes are < 0xc4.  */
717 	    for (; *string; string++)
718 	      if ((unsigned char) *string >= 0xc4)
719 		{
720 		  multiline_error (xstrdup (""),
721 				   xstrdup (_("\
722 message catalog has msgid strings containing characters outside ISO-8859-1\n\
723 but the Qt message catalog format supports Unicode only in the translated\n\
724 strings, not in the untranslated strings\n")));
725 		  return 1;
726 		}
727 	  }
728       }
729 
730       if (strcmp (domain_name, "-") == 0)
731 	{
732 	  output_file = stdout;
733 	  SET_BINARY (fileno (output_file));
734 	}
735       else
736 	{
737 	  output_file = fopen (file_name, "wb");
738 	  if (output_file == NULL)
739 	    {
740 	      error (0, errno, _("error while opening \"%s\" for writing"),
741 		     file_name);
742 	      return 1;
743 	    }
744 	}
745 
746       if (output_file != NULL)
747 	{
748 	  write_qm (output_file, mlp);
749 
750 	  /* Make sure nothing went wrong.  */
751 	  if (fwriteerror (output_file))
752 	    error (EXIT_FAILURE, errno, _("error while writing \"%s\" file"),
753 		   file_name);
754 	}
755     }
756 
757   return 0;
758 }
759