facf0c92e0
Red Bear OS is a full fork. All sources must be available from git clone with zero network access. Removed gitignore rules that excluded fetched source trees under recipes/*/source/, local/recipes/kde/*/source/, local/recipes/qt/*/source/, and vendor source trees. Build artifacts (target/, build/, source.tar, *.o, *.so) remain excluded. 127291 files added — kernel, relibc, base, bootloader, pkgar, all KDE/Qt frameworks, mesa, wayland, DRM drivers, and every other recipe source.
479 lines
14 KiB
C
479 lines
14 KiB
C
/* GNU gettext - internationalization aids
|
|
Copyright (C) 1995-1998, 2000-2008, 2012, 2019-2020 Free Software
|
|
Foundation, Inc.
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
# include <config.h>
|
|
#endif
|
|
|
|
/* Specification. */
|
|
#include "write-catalog.h"
|
|
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <limits.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include <unistd.h>
|
|
#ifndef STDOUT_FILENO
|
|
# define STDOUT_FILENO 1
|
|
#endif
|
|
|
|
#include <textstyle.h>
|
|
|
|
#include "fwriteerror.h"
|
|
#include "error-progname.h"
|
|
#include "xvasprintf.h"
|
|
#include "po-xerror.h"
|
|
#include "gettext.h"
|
|
|
|
/* Our regular abbreviation. */
|
|
#define _(str) gettext (str)
|
|
|
|
/* When compiled in src, enable color support.
|
|
When compiled in libgettextpo, don't enable color support. */
|
|
#ifdef GETTEXTDATADIR
|
|
|
|
# define ENABLE_COLOR 1
|
|
|
|
# include "relocatable.h"
|
|
# include "po-charset.h"
|
|
# include "msgl-iconv.h"
|
|
|
|
# define GETTEXTSTYLESDIR GETTEXTDATADIR "/styles"
|
|
|
|
#endif
|
|
|
|
|
|
/* =========== Some parameters for use by 'msgdomain_list_print'. ========== */
|
|
|
|
|
|
/* This variable controls the page width when printing messages.
|
|
Defaults to PAGE_WIDTH if not set. Zero (0) given to message_page_-
|
|
width_set will result in no wrapping being performed. */
|
|
static size_t page_width = PAGE_WIDTH;
|
|
|
|
void
|
|
message_page_width_set (size_t n)
|
|
{
|
|
if (n == 0)
|
|
{
|
|
page_width = INT_MAX;
|
|
return;
|
|
}
|
|
|
|
if (n < 20)
|
|
n = 20;
|
|
|
|
page_width = n;
|
|
}
|
|
|
|
|
|
/* ======================== msgdomain_list_print() ======================== */
|
|
|
|
|
|
void
|
|
msgdomain_list_print (msgdomain_list_ty *mdlp, const char *filename,
|
|
catalog_output_format_ty output_syntax,
|
|
bool force, bool debug)
|
|
{
|
|
bool to_stdout;
|
|
|
|
/* We will not write anything if, for every domain, we have no message
|
|
or only the header entry. */
|
|
if (!force)
|
|
{
|
|
bool found_nonempty = false;
|
|
size_t k;
|
|
|
|
for (k = 0; k < mdlp->nitems; k++)
|
|
{
|
|
message_list_ty *mlp = mdlp->item[k]->messages;
|
|
|
|
if (!(mlp->nitems == 0
|
|
|| (mlp->nitems == 1 && is_header (mlp->item[0]))))
|
|
{
|
|
found_nonempty = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!found_nonempty)
|
|
return;
|
|
}
|
|
|
|
/* Check whether the output format can accommodate all messages. */
|
|
if (!output_syntax->supports_multiple_domains && mdlp->nitems > 1)
|
|
{
|
|
if (output_syntax->alternative_is_po)
|
|
po_xerror (PO_SEVERITY_FATAL_ERROR, NULL, NULL, 0, 0, false,
|
|
_("Cannot output multiple translation domains into a single file with the specified output format. Try using PO file syntax instead."));
|
|
else
|
|
po_xerror (PO_SEVERITY_FATAL_ERROR, NULL, NULL, 0, 0, false,
|
|
_("Cannot output multiple translation domains into a single file with the specified output format."));
|
|
}
|
|
else
|
|
{
|
|
if (!output_syntax->supports_contexts)
|
|
{
|
|
const lex_pos_ty *has_context;
|
|
size_t k;
|
|
|
|
has_context = NULL;
|
|
for (k = 0; k < mdlp->nitems; k++)
|
|
{
|
|
message_list_ty *mlp = mdlp->item[k]->messages;
|
|
size_t j;
|
|
|
|
for (j = 0; j < mlp->nitems; j++)
|
|
{
|
|
message_ty *mp = mlp->item[j];
|
|
|
|
if (mp->msgctxt != NULL)
|
|
{
|
|
has_context = &mp->pos;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (has_context != NULL)
|
|
{
|
|
error_with_progname = false;
|
|
po_xerror (PO_SEVERITY_FATAL_ERROR, NULL,
|
|
has_context->file_name, has_context->line_number,
|
|
(size_t)(-1), false,
|
|
_("message catalog has context dependent translations, but the output format does not support them."));
|
|
error_with_progname = true;
|
|
}
|
|
}
|
|
|
|
if (!output_syntax->supports_plurals)
|
|
{
|
|
const lex_pos_ty *has_plural;
|
|
size_t k;
|
|
|
|
has_plural = NULL;
|
|
for (k = 0; k < mdlp->nitems; k++)
|
|
{
|
|
message_list_ty *mlp = mdlp->item[k]->messages;
|
|
size_t j;
|
|
|
|
for (j = 0; j < mlp->nitems; j++)
|
|
{
|
|
message_ty *mp = mlp->item[j];
|
|
|
|
if (mp->msgid_plural != NULL)
|
|
{
|
|
has_plural = &mp->pos;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (has_plural != NULL)
|
|
{
|
|
error_with_progname = false;
|
|
if (output_syntax->alternative_is_java_class)
|
|
po_xerror (PO_SEVERITY_FATAL_ERROR, NULL,
|
|
has_plural->file_name, has_plural->line_number,
|
|
(size_t)(-1), false,
|
|
_("message catalog has plural form translations, but the output format does not support them. Try generating a Java class using \"msgfmt --java\", instead of a properties file."));
|
|
else
|
|
po_xerror (PO_SEVERITY_FATAL_ERROR, NULL,
|
|
has_plural->file_name, has_plural->line_number,
|
|
(size_t)(-1), false,
|
|
_("message catalog has plural form translations, but the output format does not support them."));
|
|
error_with_progname = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
to_stdout = (filename == NULL || strcmp (filename, "-") == 0
|
|
|| strcmp (filename, "/dev/stdout") == 0);
|
|
|
|
#if ENABLE_COLOR
|
|
if (output_syntax->supports_color
|
|
&& (color_mode == color_yes
|
|
|| (color_mode == color_tty && to_stdout
|
|
&& isatty (STDOUT_FILENO)
|
|
&& getenv ("NO_COLOR") == NULL)))
|
|
{
|
|
int fd;
|
|
ostream_t stream;
|
|
|
|
/* Open the output file. */
|
|
if (!to_stdout)
|
|
{
|
|
fd = open (filename, O_WRONLY | O_CREAT | O_TRUNC,
|
|
/* 0666 in portable POSIX notation: */
|
|
S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
|
|
if (fd < 0)
|
|
{
|
|
const char *errno_description = strerror (errno);
|
|
po_xerror (PO_SEVERITY_FATAL_ERROR, NULL, NULL, 0, 0, false,
|
|
xasprintf ("%s: %s",
|
|
xasprintf (_("cannot create output file \"%s\""),
|
|
filename),
|
|
errno_description));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
fd = STDOUT_FILENO;
|
|
filename = _("standard output");
|
|
}
|
|
|
|
style_file_prepare ("PO_STYLE",
|
|
"GETTEXTSTYLESDIR", relocate (GETTEXTSTYLESDIR),
|
|
"po-default.css");
|
|
stream =
|
|
styled_ostream_create (fd, filename, TTYCTL_AUTO, style_file_name);
|
|
output_syntax->print (mdlp, stream, page_width, debug);
|
|
ostream_free (stream);
|
|
|
|
/* Make sure nothing went wrong. */
|
|
if (close (fd) < 0)
|
|
{
|
|
const char *errno_description = strerror (errno);
|
|
po_xerror (PO_SEVERITY_FATAL_ERROR, NULL, NULL, 0, 0, false,
|
|
xasprintf ("%s: %s",
|
|
xasprintf (_("error while writing \"%s\" file"),
|
|
filename),
|
|
errno_description));
|
|
}
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
FILE *fp;
|
|
file_ostream_t stream;
|
|
|
|
/* Open the output file. */
|
|
if (!to_stdout)
|
|
{
|
|
fp = fopen (filename, "wb");
|
|
if (fp == NULL)
|
|
{
|
|
const char *errno_description = strerror (errno);
|
|
po_xerror (PO_SEVERITY_FATAL_ERROR, NULL, NULL, 0, 0, false,
|
|
xasprintf ("%s: %s",
|
|
xasprintf (_("cannot create output file \"%s\""),
|
|
filename),
|
|
errno_description));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
fp = stdout;
|
|
filename = _("standard output");
|
|
}
|
|
|
|
stream = file_ostream_create (fp);
|
|
|
|
#if ENABLE_COLOR
|
|
if (output_syntax->supports_color && color_mode == color_html)
|
|
{
|
|
html_styled_ostream_t html_stream;
|
|
|
|
/* Convert mdlp to UTF-8 encoding. */
|
|
if (mdlp->encoding != po_charset_utf8)
|
|
{
|
|
mdlp = msgdomain_list_copy (mdlp, 0);
|
|
mdlp = iconv_msgdomain_list (mdlp, po_charset_utf8, false, NULL);
|
|
}
|
|
|
|
style_file_prepare ("PO_STYLE",
|
|
"GETTEXTSTYLESDIR", relocate (GETTEXTSTYLESDIR),
|
|
"po-default.css");
|
|
html_stream = html_styled_ostream_create (stream, style_file_name);
|
|
output_syntax->print (mdlp, html_stream, page_width, debug);
|
|
ostream_free (html_stream);
|
|
}
|
|
else
|
|
{
|
|
noop_styled_ostream_t styled_stream;
|
|
|
|
styled_stream = noop_styled_ostream_create (stream, false);
|
|
output_syntax->print (mdlp, styled_stream, page_width, debug);
|
|
ostream_free (styled_stream);
|
|
}
|
|
#else
|
|
output_syntax->print (mdlp, stream, page_width, debug);
|
|
/* Don't call ostream_free if file_ostream_create is a dummy. */
|
|
if (stream != fp)
|
|
#endif
|
|
ostream_free (stream);
|
|
|
|
/* Make sure nothing went wrong. */
|
|
if (fwriteerror (fp))
|
|
{
|
|
const char *errno_description = strerror (errno);
|
|
po_xerror (PO_SEVERITY_FATAL_ERROR, NULL, NULL, 0, 0, false,
|
|
xasprintf ("%s: %s",
|
|
xasprintf (_("error while writing \"%s\" file"),
|
|
filename),
|
|
errno_description));
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/* =============================== Sorting. ================================ */
|
|
|
|
|
|
static int
|
|
cmp_by_msgid (const void *va, const void *vb)
|
|
{
|
|
const message_ty *a = *(const message_ty **) va;
|
|
const message_ty *b = *(const message_ty **) vb;
|
|
|
|
/* Because msgids normally contain only ASCII characters or are UTF-8
|
|
encoded, it is OK to sort them as if we were in a C.UTF-8 locale. And
|
|
strcoll() in a C.UTF-8 locale is the same as strcmp(). */
|
|
int cmp = strcmp (a->msgid, b->msgid);
|
|
if (cmp != 0)
|
|
return cmp;
|
|
|
|
/* If the msgids are equal, disambiguate by comparing the contexts. */
|
|
if (a->msgctxt == b->msgctxt)
|
|
return 0;
|
|
if (a->msgctxt == NULL)
|
|
return -1;
|
|
if (b->msgctxt == NULL)
|
|
return 1;
|
|
return strcmp (a->msgctxt, b->msgctxt);
|
|
}
|
|
|
|
|
|
void
|
|
msgdomain_list_sort_by_msgid (msgdomain_list_ty *mdlp)
|
|
{
|
|
size_t k;
|
|
|
|
for (k = 0; k < mdlp->nitems; k++)
|
|
{
|
|
message_list_ty *mlp = mdlp->item[k]->messages;
|
|
|
|
if (mlp->nitems > 0)
|
|
qsort (mlp->item, mlp->nitems, sizeof (mlp->item[0]), cmp_by_msgid);
|
|
}
|
|
}
|
|
|
|
|
|
/* Sort the file positions of every message. */
|
|
|
|
static int
|
|
cmp_filepos (const void *va, const void *vb)
|
|
{
|
|
const lex_pos_ty *a = (const lex_pos_ty *) va;
|
|
const lex_pos_ty *b = (const lex_pos_ty *) vb;
|
|
int cmp;
|
|
|
|
cmp = strcmp (a->file_name, b->file_name);
|
|
if (cmp == 0)
|
|
cmp = (int) a->line_number - (int) b->line_number;
|
|
|
|
return cmp;
|
|
}
|
|
|
|
static void
|
|
msgdomain_list_sort_filepos (msgdomain_list_ty *mdlp)
|
|
{
|
|
size_t j, k;
|
|
|
|
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];
|
|
|
|
if (mp->filepos_count > 0)
|
|
qsort (mp->filepos, mp->filepos_count, sizeof (mp->filepos[0]),
|
|
cmp_filepos);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/* Sort the messages according to the file position. */
|
|
|
|
static int
|
|
cmp_by_filepos (const void *va, const void *vb)
|
|
{
|
|
const message_ty *a = *(const message_ty **) va;
|
|
const message_ty *b = *(const message_ty **) vb;
|
|
int cmp;
|
|
|
|
/* No filepos is smaller than any other filepos. */
|
|
cmp = (a->filepos_count != 0) - (b->filepos_count != 0);
|
|
if (cmp != 0)
|
|
return cmp;
|
|
|
|
if (a->filepos_count != 0)
|
|
{
|
|
/* Compare on the file names... */
|
|
cmp = strcmp (a->filepos[0].file_name, b->filepos[0].file_name);
|
|
if (cmp != 0)
|
|
return cmp;
|
|
|
|
/* If they are equal, compare on the line numbers... */
|
|
cmp = a->filepos[0].line_number - b->filepos[0].line_number;
|
|
if (cmp != 0)
|
|
return cmp;
|
|
}
|
|
|
|
/* If they are equal, compare on the msgid strings. */
|
|
/* Because msgids normally contain only ASCII characters or are UTF-8
|
|
encoded, it is OK to sort them as if we were in a C.UTF-8 locale. And
|
|
strcoll() in a C.UTF-8 locale is the same as strcmp(). */
|
|
cmp = strcmp (a->msgid, b->msgid);
|
|
if (cmp != 0)
|
|
return cmp;
|
|
|
|
/* If the msgids are equal, disambiguate by comparing the contexts. */
|
|
if (a->msgctxt == b->msgctxt)
|
|
return 0;
|
|
if (a->msgctxt == NULL)
|
|
return -1;
|
|
if (b->msgctxt == NULL)
|
|
return 1;
|
|
return strcmp (a->msgctxt, b->msgctxt);
|
|
}
|
|
|
|
|
|
void
|
|
msgdomain_list_sort_by_filepos (msgdomain_list_ty *mdlp)
|
|
{
|
|
size_t k;
|
|
|
|
/* It makes sense to compare filepos[0] of different messages only after
|
|
the filepos[] array of each message has been sorted. Sort it now. */
|
|
msgdomain_list_sort_filepos (mdlp);
|
|
|
|
for (k = 0; k < mdlp->nitems; k++)
|
|
{
|
|
message_list_ty *mlp = mdlp->item[k]->messages;
|
|
|
|
if (mlp->nitems > 0)
|
|
qsort (mlp->item, mlp->nitems, sizeof (mlp->item[0]), cmp_by_filepos);
|
|
}
|
|
}
|