#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include "write-qt.h"
#include <assert.h>
#include <errno.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "error.h"
#include "xerror.h"
#include "message.h"
#include "po-charset.h"
#include "msgl-iconv.h"
#include "hash-string.h"
#include "utf8-ucs4.h"
#include "xalloc.h"
#include "obstack.h"
#include "binary-io.h"
#include "fwriteerror.h"
#include "exit.h"
#include "gettext.h"
#define _(str) gettext (str)
static inline void
write_u8 (FILE *output_file, unsigned char value)
{
putc (value, output_file);
}
static inline void
write_u16 (FILE *output_file, unsigned short value)
{
unsigned char data[2];
data[0] = (value >> 8) & 0xff;
data[1] = value & 0xff;
fwrite (data, 2, 1, output_file);
}
static inline void
write_u32 (FILE *output_file, unsigned int value)
{
unsigned char data[4];
data[0] = (value >> 24) & 0xff;
data[1] = (value >> 16) & 0xff;
data[2] = (value >> 8) & 0xff;
data[3] = value & 0xff;
fwrite (data, 4, 1, output_file);
}
#define obstack_chunk_alloc xmalloc
#define obstack_chunk_free free
static void
append_u8 (struct obstack *mempool, unsigned char value)
{
unsigned char data[1];
data[0] = value;
obstack_grow (mempool, data, 1);
}
static void
append_u16 (struct obstack *mempool, unsigned short value)
{
unsigned char data[2];
data[0] = (value >> 8) & 0xff;
data[1] = value & 0xff;
obstack_grow (mempool, data, 2);
}
static void
append_u32 (struct obstack *mempool, unsigned int value)
{
unsigned char data[4];
data[0] = (value >> 24) & 0xff;
data[1] = (value >> 16) & 0xff;
data[2] = (value >> 8) & 0xff;
data[3] = value & 0xff;
obstack_grow (mempool, data, 4);
}
static void
append_base_string (struct obstack *mempool, const char *string)
{
size_t length = strlen (string) + 1;
append_u32 (mempool, length);
obstack_grow (mempool, string, length);
}
static void
append_unicode_string (struct obstack *mempool, const unsigned short *string,
size_t length)
{
append_u32 (mempool, length * 2);
for (; length > 0; string++, length--)
append_u16 (mempool, *string);
}
static inline unsigned int
peek_u32 (const unsigned char *p)
{
return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
}
static char *
conv_to_iso_8859_1 (const char *string)
{
size_t length = strlen (string);
const char *str = string;
const char *str_limit = string + length;
char *result = (char *) xmalloc (length + 1);
char *q = result;
while (str < str_limit)
{
unsigned int uc;
str += u8_mbtouc (&uc, (const unsigned char *) str, str_limit - str);
if (!(uc < 0x100))
abort ();
*q++ = (unsigned char) uc;
}
*q = '\0';
assert (q - result <= length);
return result;
}
static unsigned short *
conv_to_utf16 (const char *string, size_t *sizep)
{
size_t length = strlen (string);
const char *str = string;
const char *str_limit = string + length;
unsigned short *result = (unsigned short *) xmalloc (2 * length);
unsigned short *q = result;
while (str < str_limit)
{
unsigned int uc;
str += u8_mbtouc (&uc, (const unsigned char *) str, str_limit - str);
if (uc < 0x10000)
*q++ = (unsigned short) uc;
else
{
*q++ = 0xd800 + ((uc - 0x10000) >> 10);
*q++ = 0xdc00 + ((uc - 0x10000) & 0x3ff);
}
}
assert (q - result <= 2 * length);
*sizep = q - result;
return result;
}
static unsigned int
string_hashcode (const char *str)
{
unsigned int h;
h = hash_string (str);
if (h == 0)
h = 1;
return h;
}
static int
cmp_hashes (const void *va, const void *vb)
{
const unsigned char *a = (const unsigned char *) va;
const unsigned char *b = (const unsigned char *) vb;
unsigned int a_hashcode = peek_u32 (a);
unsigned int b_hashcode = peek_u32 (b);
if (a_hashcode != b_hashcode)
return (a_hashcode >= b_hashcode ? 1 : -1);
else
{
unsigned int a_offset = peek_u32 (a + 4);
unsigned int b_offset = peek_u32 (b + 4);
if (a_offset != b_offset)
return (a_offset >= b_offset ? 1 : -1);
else
return 0;
}
}
static void
write_section (FILE *output_file, unsigned char tag, void *data, size_t size)
{
if (size > 0)
{
write_u8 (output_file, tag);
write_u32 (output_file, size);
fwrite (data, size, 1, output_file);
}
}
static void
write_qm (FILE *output_file, message_list_ty *mlp)
{
static unsigned char magic[16] =
{
0x3C, 0xB8, 0x64, 0x18, 0xCA, 0xEF, 0x9C, 0x95,
0xCD, 0x21, 0x1C, 0xBF, 0x60, 0xA1, 0xBD, 0xDD
};
struct obstack hashes_pool;
struct obstack messages_pool;
size_t j;
obstack_init (&hashes_pool);
obstack_init (&messages_pool);
for (j = 0; j < mlp->nitems; j++)
{
message_ty *mp = mlp->item[j];
if (mp->msgid[0] != '\0')
{
char *msgid_as_iso_8859_1 = conv_to_iso_8859_1 (mp->msgid);
size_t msgstr_len;
unsigned short *msgstr_as_utf16 =
conv_to_utf16 (mp->msgstr, &msgstr_len);
unsigned int hashcode = string_hashcode (msgid_as_iso_8859_1);
unsigned int offset = obstack_object_size (&messages_pool);
append_u32 (&hashes_pool, hashcode);
append_u32 (&hashes_pool, offset);
append_u8 (&messages_pool, 0x03);
append_unicode_string (&messages_pool, msgstr_as_utf16, msgstr_len);
append_u8 (&messages_pool, 0x08);
append_base_string (&messages_pool, "");
append_u8 (&messages_pool, 0x06);
append_base_string (&messages_pool, msgid_as_iso_8859_1);
append_u8 (&messages_pool, 0x07);
append_base_string (&messages_pool, "");
append_u8 (&messages_pool, 0x05);
append_u32 (&messages_pool, hashcode);
append_u8 (&messages_pool, 0x01);
free (msgstr_as_utf16);
free (msgid_as_iso_8859_1);
}
}
{
size_t nstrings = obstack_object_size (&hashes_pool) / 8;
if (nstrings > 0)
qsort (obstack_base (&hashes_pool), nstrings, 8, cmp_hashes);
}
fwrite (magic, sizeof (magic), 1, output_file);
write_section (output_file, 0x42, obstack_base (&hashes_pool),
obstack_object_size (&hashes_pool));
write_section (output_file, 0x69, obstack_base (&messages_pool),
obstack_object_size (&messages_pool));
#if 0
write_section (output_file, 0x2f, ...);
#endif
obstack_free (&messages_pool, NULL);
obstack_free (&hashes_pool, NULL);
}
int
msgdomain_write_qt (message_list_ty *mlp, const char *canon_encoding,
const char *domain_name, const char *file_name)
{
FILE *output_file;
if (mlp->nitems != 0)
{
{
bool has_plural;
size_t j;
has_plural = false;
for (j = 0; j < mlp->nitems; j++)
if (mlp->item[j]->msgid_plural != NULL)
has_plural = true;
if (has_plural)
{
multiline_error (xstrdup (""),
xstrdup (_("\
message catalog has plural form translations\n\
but the Qt message catalog format doesn't support plural handling\n")));
return 1;
}
}
iconv_message_list (mlp, canon_encoding, po_charset_utf8, NULL);
{
size_t j;
for (j = 0; j < mlp->nitems; j++)
{
const char *string = mlp->item[j]->msgid;
for (; *string; string++)
if ((unsigned char) *string >= 0xc4)
{
multiline_error (xstrdup (""),
xstrdup (_("\
message catalog has msgid strings containing characters outside ISO-8859-1\n\
but the Qt message catalog format supports Unicode only in the translated\n\
strings, not in the untranslated strings\n")));
return 1;
}
}
}
if (strcmp (domain_name, "-") == 0)
{
output_file = stdout;
SET_BINARY (fileno (output_file));
}
else
{
output_file = fopen (file_name, "wb");
if (output_file == NULL)
{
error (0, errno, _("error while opening \"%s\" for writing"),
file_name);
return 1;
}
}
if (output_file != NULL)
{
write_qm (output_file, mlp);
if (fwriteerror (output_file))
error (EXIT_FAILURE, errno, _("error while writing \"%s\" file"),
file_name);
if (output_file != stdout)
fclose (output_file);
}
}
return 0;
}