#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <alloca.h>
#include "msgl-cat.h"
#include <stdbool.h>
#include <stddef.h>
#include <string.h>
#include "error.h"
#include "xerror.h"
#include "message.h"
#include "read-po.h"
#include "po-charset.h"
#include "msgl-ascii.h"
#include "msgl-equal.h"
#include "msgl-iconv.h"
#include "xalloc.h"
#include "strstr.h"
#include "basename.h"
#include "exit.h"
#include "gettext.h"
#define _(str) gettext (str)
int more_than;
int less_than;
bool use_first;
bool msgcomm_mode = false;
bool omit_header = false;
static bool
is_message_selected (const message_ty *tmp)
{
int used = (tmp->used >= 0 ? tmp->used : - tmp->used);
return (tmp->msgid[0] == '\0'
? !omit_header
: (used > more_than && used < less_than));
}
static bool
is_message_needed (const message_ty *mp)
{
if (!msgcomm_mode
&& ((mp->msgid[0] != '\0' && mp->is_fuzzy) || mp->msgstr[0] == '\0'))
return mp->tmp->used < 0 && is_message_selected (mp->tmp);
else
return is_message_selected (mp->tmp);
}
static bool
is_message_first_needed (const message_ty *mp)
{
if (mp->tmp->obsolete && is_message_needed (mp))
{
mp->tmp->obsolete = false;
return true;
}
else
return false;
}
msgdomain_list_ty *
catenate_msgdomain_list (string_list_ty *file_list, const char *to_code)
{
const char * const *files = file_list->item;
size_t nfiles = file_list->nitems;
msgdomain_list_ty **mdlps;
const char ***canon_charsets;
const char ***identifications;
msgdomain_list_ty *total_mdlp;
const char *canon_to_code;
size_t n, j, k;
mdlps =
(msgdomain_list_ty **) xmalloc (nfiles * sizeof (msgdomain_list_ty *));
for (n = 0; n < nfiles; n++)
mdlps[n] = read_po_file (files[n]);
canon_charsets = (const char ***) xmalloc (nfiles * sizeof (const char **));
for (n = 0; n < nfiles; n++)
{
msgdomain_list_ty *mdlp = mdlps[n];
size_t k;
canon_charsets[n] =
(const char **) xmalloc (mdlp->nitems * sizeof (const char *));
for (k = 0; k < mdlp->nitems; k++)
{
message_list_ty *mlp = mdlp->item[k]->messages;
const char *canon_from_code = NULL;
if (mlp->nitems > 0)
{
for (j = 0; j < mlp->nitems; j++)
if (mlp->item[j]->msgid[0] == '\0' && !mlp->item[j]->obsolete)
{
const char *header = mlp->item[j]->msgstr;
if (header != NULL)
{
const char *charsetstr = strstr (header, "charset=");
if (charsetstr != NULL)
{
size_t len;
char *charset;
const char *canon_charset;
charsetstr += strlen ("charset=");
len = strcspn (charsetstr, " \t\n");
charset = (char *) alloca (len + 1);
memcpy (charset, charsetstr, len);
charset[len] = '\0';
canon_charset = po_charset_canonicalize (charset);
if (canon_charset == NULL)
{
const char *filename = files[n];
size_t filenamelen = strlen (filename);
if (filenamelen >= 4
&& memcmp (filename + filenamelen - 4,
".pot", 4) == 0
&& strcmp (charset, "CHARSET") == 0)
canon_charset = po_charset_ascii;
else
error (EXIT_FAILURE, 0,
_("\
present charset \"%s\" is not a portable encoding name"),
charset);
}
if (canon_from_code == NULL)
canon_from_code = canon_charset;
else if (canon_from_code != canon_charset)
error (EXIT_FAILURE, 0,
_("\
two different charsets \"%s\" and \"%s\" in input file"),
canon_from_code, canon_charset);
}
}
}
if (canon_from_code == NULL)
{
if (is_ascii_message_list (mlp))
canon_from_code = po_charset_ascii;
else if (mdlp->encoding != NULL)
canon_from_code = mdlp->encoding;
else
{
if (k == 0)
error (EXIT_FAILURE, 0, _("\
input file `%s' doesn't contain a header entry with a charset specification"),
files[n]);
else
error (EXIT_FAILURE, 0, _("\
domain \"%s\" in input file `%s' doesn't contain a header entry with a charset specification"),
mdlp->item[k]->domain, files[n]);
}
}
}
canon_charsets[n][k] = canon_from_code;
}
}
identifications = (const char ***) xmalloc (nfiles * sizeof (const char **));
for (n = 0; n < nfiles; n++)
{
const char *filename = basename (files[n]);
msgdomain_list_ty *mdlp = mdlps[n];
size_t k;
identifications[n] =
(const char **) xmalloc (mdlp->nitems * sizeof (const char *));
for (k = 0; k < mdlp->nitems; k++)
{
const char *domain = mdlp->item[k]->domain;
message_list_ty *mlp = mdlp->item[k]->messages;
char *project_id = NULL;
for (j = 0; j < mlp->nitems; j++)
if (mlp->item[j]->msgid[0] == '\0' && !mlp->item[j]->obsolete)
{
const char *header = mlp->item[j]->msgstr;
if (header != NULL)
{
const char *cp = strstr (header, "Project-Id-Version:");
if (cp != NULL)
{
const char *endp;
cp += sizeof ("Project-Id-Version:") - 1;
endp = strchr (cp, '\n');
if (endp == NULL)
endp = cp + strlen (cp);
while (cp < endp && *cp == ' ')
cp++;
if (cp < endp)
{
size_t len = endp - cp;
project_id = (char *) xmalloc (len + 1);
memcpy (project_id, cp, len);
project_id[len] = '\0';
}
break;
}
}
}
identifications[n][k] =
(project_id != NULL
? (k > 0 ? xasprintf ("%s:%s (%s)", filename, domain, project_id)
: xasprintf ("%s (%s)", filename, project_id))
: (k > 0 ? xasprintf ("%s:%s", filename, domain)
: xasprintf ("%s", filename)));
}
}
total_mdlp = msgdomain_list_alloc (true);
for (n = 0; n < nfiles; n++)
{
msgdomain_list_ty *mdlp = mdlps[n];
for (k = 0; k < mdlp->nitems; k++)
{
const char *domain = mdlp->item[k]->domain;
message_list_ty *mlp = mdlp->item[k]->messages;
message_list_ty *total_mlp;
total_mlp = msgdomain_list_sublist (total_mdlp, domain, true);
for (j = 0; j < mlp->nitems; j++)
{
message_ty *mp = mlp->item[j];
message_ty *tmp;
size_t i;
tmp = message_list_search (total_mlp, mp->msgid);
if (tmp == NULL)
{
tmp = message_alloc (mp->msgid, mp->msgid_plural, NULL, 0,
&mp->pos);
tmp->is_fuzzy = true;
for (i = 0; i < NFORMATS; i++)
tmp->is_format[i] = undecided;
tmp->do_wrap = yes;
tmp->obsolete = true;
tmp->alternative_count = 0;
tmp->alternative = NULL;
message_list_append (total_mlp, tmp);
}
if (!msgcomm_mode
&& ((mp->msgid[0] != '\0' && mp->is_fuzzy)
|| mp->msgstr[0] == '\0'))
{
if (tmp->used <= 0)
tmp->used--;
}
else
{
if (tmp->used < 0)
tmp->used = 0;
tmp->used++;
}
mp->tmp = tmp;
}
}
}
for (n = 0; n < nfiles; n++)
{
msgdomain_list_ty *mdlp = mdlps[n];
for (k = 0; k < mdlp->nitems; k++)
{
message_list_ty *mlp = mdlp->item[k]->messages;
message_list_remove_if_not (mlp,
use_first
? is_message_first_needed
: is_message_needed);
if (mlp->nitems == 0)
canon_charsets[n][k] = NULL;
}
}
for (k = 0; k < total_mdlp->nitems; k++)
{
message_list_ty *mlp = total_mdlp->item[k]->messages;
message_list_remove_if_not (mlp, is_message_selected);
}
if (nfiles > 0)
{
bool all_same_encoding = true;
for (n = 1; n < nfiles; n++)
if (mdlps[n]->encoding != mdlps[0]->encoding)
{
all_same_encoding = false;
break;
}
if (all_same_encoding)
total_mdlp->encoding = mdlps[0]->encoding;
}
if (to_code != NULL)
{
canon_to_code = po_charset_canonicalize (to_code);
if (canon_to_code == NULL)
error (EXIT_FAILURE, 0,
_("target charset \"%s\" is not a portable encoding name."),
to_code);
}
else
{
const char *first = NULL;
const char *second = NULL;
bool with_ASCII = false;
bool with_UTF8 = false;
bool all_ASCII_compatible = true;
for (n = 0; n < nfiles; n++)
{
msgdomain_list_ty *mdlp = mdlps[n];
for (k = 0; k < mdlp->nitems; k++)
if (canon_charsets[n][k] != NULL)
{
if (canon_charsets[n][k] == po_charset_ascii)
with_ASCII = true;
else
{
if (first == NULL)
first = canon_charsets[n][k];
else if (canon_charsets[n][k] != first && second == NULL)
second = canon_charsets[n][k];
if (strcmp (canon_charsets[n][k], "UTF-8") == 0)
with_UTF8 = true;
if (!po_charset_ascii_compatible (canon_charsets[n][k]))
all_ASCII_compatible = false;
}
}
}
if (with_ASCII && !all_ASCII_compatible)
{
if (second == NULL)
second = po_charset_ascii;
}
if (second != NULL)
{
if (with_UTF8)
multiline_warning (xasprintf (_("warning: ")),
xasprintf (_("\
Input files contain messages in different encodings, UTF-8 among others.\n\
Converting the output to UTF-8.\n\
")));
else
multiline_warning (xasprintf (_("warning: ")),
xasprintf (_("\
Input files contain messages in different encodings, %s and %s among others.\n\
Converting the output to UTF-8.\n\
To select a different output encoding, use the --to-code option.\n\
"), first, second));
canon_to_code = po_charset_utf8;
}
else if (first != NULL && with_ASCII && all_ASCII_compatible)
{
canon_to_code = first;
}
else
{
canon_to_code = NULL;
}
}
if (canon_to_code != NULL)
for (n = 0; n < nfiles; n++)
{
msgdomain_list_ty *mdlp = mdlps[n];
for (k = 0; k < mdlp->nitems; k++)
if (canon_charsets[n][k] != NULL)
if (!(to_code == NULL && canon_charsets[n][k] == canon_to_code))
iconv_message_list (mdlp->item[k]->messages, canon_charsets[n][k],
canon_to_code, files[n]);
}
for (n = 0; n < nfiles; n++)
{
msgdomain_list_ty *mdlp = mdlps[n];
for (k = 0; k < mdlp->nitems; k++)
{
message_list_ty *mlp = mdlp->item[k]->messages;
for (j = 0; j < mlp->nitems; j++)
{
message_ty *mp = mlp->item[j];
message_ty *tmp = mp->tmp;
size_t i;
if (use_first || tmp->used == 1 || tmp->used == -1)
{
tmp->msgstr = mp->msgstr;
tmp->msgstr_len = mp->msgstr_len;
tmp->pos = mp->pos;
if (mp->comment)
for (i = 0; i < mp->comment->nitems; i++)
message_comment_append (tmp, mp->comment->item[i]);
if (mp->comment_dot)
for (i = 0; i < mp->comment_dot->nitems; i++)
message_comment_dot_append (tmp,
mp->comment_dot->item[i]);
for (i = 0; i < mp->filepos_count; i++)
message_comment_filepos (tmp, mp->filepos[i].file_name,
mp->filepos[i].line_number);
tmp->is_fuzzy = mp->is_fuzzy;
for (i = 0; i < NFORMATS; i++)
tmp->is_format[i] = mp->is_format[i];
tmp->do_wrap = mp->do_wrap;
tmp->obsolete = mp->obsolete;
}
else if (msgcomm_mode)
{
if (tmp->msgstr == NULL)
{
tmp->msgstr = mp->msgstr;
tmp->msgstr_len = mp->msgstr_len;
tmp->pos = mp->pos;
tmp->is_fuzzy = mp->is_fuzzy;
}
if (mp->comment && tmp->comment == NULL)
for (i = 0; i < mp->comment->nitems; i++)
message_comment_append (tmp, mp->comment->item[i]);
if (mp->comment_dot && tmp->comment_dot == NULL)
for (i = 0; i < mp->comment_dot->nitems; i++)
message_comment_dot_append (tmp,
mp->comment_dot->item[i]);
for (i = 0; i < mp->filepos_count; i++)
message_comment_filepos (tmp, mp->filepos[i].file_name,
mp->filepos[i].line_number);
for (i = 0; i < NFORMATS; i++)
if (tmp->is_format[i] == undecided)
tmp->is_format[i] = mp->is_format[i];
if (tmp->do_wrap == undecided)
tmp->do_wrap = mp->do_wrap;
tmp->obsolete = false;
}
else
{
char *id = xasprintf ("#-#-#-#-# %s #-#-#-#-#",
identifications[n][k]);
size_t nbytes;
if (tmp->alternative_count == 0)
tmp->pos = mp->pos;
i = tmp->alternative_count;
nbytes = (i + 1) * sizeof (struct altstr);
tmp->alternative = xrealloc (tmp->alternative, nbytes);
tmp->alternative[i].msgstr = mp->msgstr;
tmp->alternative[i].msgstr_len = mp->msgstr_len;
tmp->alternative[i].msgstr_end =
tmp->alternative[i].msgstr + tmp->alternative[i].msgstr_len;
tmp->alternative[i].comment = mp->comment;
tmp->alternative[i].comment_dot = mp->comment_dot;
tmp->alternative[i].id = id;
tmp->alternative_count = i + 1;
for (i = 0; i < mp->filepos_count; i++)
message_comment_filepos (tmp, mp->filepos[i].file_name,
mp->filepos[i].line_number);
if (!mp->is_fuzzy)
tmp->is_fuzzy = false;
for (i = 0; i < NFORMATS; i++)
if (mp->is_format[i] == yes)
tmp->is_format[i] = yes;
else if (mp->is_format[i] == no
&& tmp->is_format[i] == undecided)
tmp->is_format[i] = no;
if (mp->do_wrap == no)
tmp->do_wrap = no;
if (!mp->obsolete)
tmp->obsolete = false;
}
}
}
}
for (k = 0; k < total_mdlp->nitems; k++)
{
message_list_ty *mlp = total_mdlp->item[k]->messages;
for (j = 0; j < mlp->nitems; j++)
{
message_ty *tmp = mlp->item[j];
if (tmp->alternative_count > 0)
{
struct altstr *first = &tmp->alternative[0];
size_t i;
for (i = 0; i < tmp->alternative_count; i++)
if (!(tmp->alternative[i].msgstr_len == first->msgstr_len
&& memcmp (tmp->alternative[i].msgstr, first->msgstr,
first->msgstr_len) == 0))
break;
if (i == tmp->alternative_count)
{
tmp->msgstr = first->msgstr;
tmp->msgstr_len = first->msgstr_len;
}
else
{
size_t len;
const char *p;
const char *p_end;
char *new_msgstr;
char *np;
len = 0;
for (i = 0; i < tmp->alternative_count; i++)
{
size_t id_len = strlen (tmp->alternative[i].id);
len += tmp->alternative[i].msgstr_len;
p = tmp->alternative[i].msgstr;
p_end = tmp->alternative[i].msgstr_end;
for (; p < p_end; p += strlen (p) + 1)
len += id_len + 2;
}
new_msgstr = (char *) xmalloc (len);
np = new_msgstr;
for (;;)
{
for (i = 0; i < tmp->alternative_count; i++)
if (tmp->alternative[i].msgstr
< tmp->alternative[i].msgstr_end)
break;
if (i == tmp->alternative_count)
break;
for (i = 0; i < tmp->alternative_count; i++)
if (tmp->alternative[i].msgstr
< tmp->alternative[i].msgstr_end)
{
if (np > new_msgstr && np[-1] != '\0'
&& np[-1] != '\n')
*np++ = '\n';
len = strlen (tmp->alternative[i].id);
memcpy (np, tmp->alternative[i].id, len);
np += len;
*np++ = '\n';
len = strlen (tmp->alternative[i].msgstr);
memcpy (np, tmp->alternative[i].msgstr, len);
np += len;
tmp->alternative[i].msgstr += len + 1;
}
*np++ = '\0';
}
tmp->msgstr = new_msgstr;
tmp->msgstr_len = np - new_msgstr;
tmp->is_fuzzy = true;
}
for (i = 0; i < tmp->alternative_count; i++)
if (tmp->alternative[i].comment == NULL
|| !string_list_equal (tmp->alternative[i].comment,
first->comment))
break;
if (i == tmp->alternative_count)
tmp->comment = first->comment;
else
for (i = 0; i < tmp->alternative_count; i++)
{
string_list_ty *slp = tmp->alternative[i].comment;
if (slp != NULL)
{
size_t l;
message_comment_append (tmp, tmp->alternative[i].id);
for (l = 0; l < slp->nitems; l++)
message_comment_append (tmp, slp->item[l]);
}
}
for (i = 0; i < tmp->alternative_count; i++)
if (tmp->alternative[i].comment_dot == NULL
|| !string_list_equal (tmp->alternative[i].comment_dot,
first->comment_dot))
break;
if (i == tmp->alternative_count)
tmp->comment_dot = first->comment_dot;
else
for (i = 0; i < tmp->alternative_count; i++)
{
string_list_ty *slp = tmp->alternative[i].comment_dot;
if (slp != NULL)
{
size_t l;
message_comment_dot_append (tmp,
tmp->alternative[i].id);
for (l = 0; l < slp->nitems; l++)
message_comment_dot_append (tmp, slp->item[l]);
}
}
}
}
}
return total_mdlp;
}