/
niceSOFT
/
gettext
Обзор
Документация
Войти
/
niceSOFT
/
gettext
Код
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
gettext-tools/src/write-catalog.c
443 строки
13 KB
Bruno Haible
Use streq.
08 май 2026, 00:26
08 май 2026, 00:26
f398566
Код
Авторство
О чём код?
/* GNU gettext - internationalization aids Copyright (C) 1995-2026 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/>. */ /* Written by Peter Miller, Ulrich Drepper, and Bruno Haible. */ #include <config.h> /* 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 "xvasprintf.h" #include "xstrerror.h" #include "xerror-handler.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, xerror_handler_ty xeh, bool force, bool debug) { /* We will not write anything if, for every domain, we have no message or only the header entry. */ if (!force) { bool found_nonempty = false; for (size_t 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) xeh->xerror (CAT_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 xeh->xerror (CAT_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 = NULL; for (size_t k = 0; k < mdlp->nitems; k++) { message_list_ty *mlp = mdlp->item[k]->messages; for (size_t 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) xeh->xerror (CAT_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.")); } if (!output_syntax->supports_plurals) { const lex_pos_ty *has_plural = NULL; for (size_t k = 0; k < mdlp->nitems; k++) { message_list_ty *mlp = mdlp->item[k]->messages; for (size_t 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) { if (output_syntax->alternative_is_java_class) xeh->xerror (CAT_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 xeh->xerror (CAT_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.")); } } } bool to_stdout = (filename == NULL || streq (filename, "-") || streq (filename, "/dev/stdout")); #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))) { /* Open the output file. */ int fd; 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) { int err = errno; xeh->xerror (CAT_SEVERITY_FATAL_ERROR, NULL, NULL, 0, 0, false, xstrerror (xasprintf (_("cannot create output file \"%s\""), filename), err)); } } else { fd = STDOUT_FILENO; filename = _("standard output"); } style_file_prepare ("PO_STYLE", "GETTEXTSTYLESDIR", relocate (GETTEXTSTYLESDIR), "po-default.css"); ostream_t stream = styled_ostream_create (fd, filename, TTYCTL_AUTO, style_file_name); output_syntax->print (mdlp, stream, page_width, xeh, debug); ostream_free (stream); /* Make sure nothing went wrong. */ if (close (fd) < 0) { int err = errno; xeh->xerror (CAT_SEVERITY_FATAL_ERROR, NULL, NULL, 0, 0, false, xstrerror (xasprintf (_("error while writing \"%s\" file"), filename), err)); } } else #endif { /* Open the output file. */ FILE *fp; if (!to_stdout) { fp = fopen (filename, "wb"); if (fp == NULL) { int err = errno; xeh->xerror (CAT_SEVERITY_FATAL_ERROR, NULL, NULL, 0, 0, false, xstrerror (xasprintf (_("cannot create output file \"%s\""), filename), err)); } } else { fp = stdout; filename = _("standard output"); } file_ostream_t stream = file_ostream_create (fp); #if ENABLE_COLOR if (output_syntax->supports_color && color_mode == color_html) { /* 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, xeh); } style_file_prepare ("PO_STYLE", "GETTEXTSTYLESDIR", relocate (GETTEXTSTYLESDIR), "po-default.css"); html_styled_ostream_t html_stream = html_styled_ostream_create (stream, style_file_name); output_syntax->print (mdlp, html_stream, page_width, xeh, debug); ostream_free (html_stream); } else { noop_styled_ostream_t styled_stream = noop_styled_ostream_create (stream, false); output_syntax->print (mdlp, styled_stream, page_width, xeh, debug); ostream_free (styled_stream); } #else output_syntax->print (mdlp, stream, page_width, xeh, 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)) { int err = errno; xeh->xerror (CAT_SEVERITY_FATAL_ERROR, NULL, NULL, 0, 0, false, xstrerror (xasprintf (_("error while writing \"%s\" file"), filename), err)); } } } /* =============================== 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) { for (size_t 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 = 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) { for (size_t k = 0; k < mdlp->nitems; k++) { message_list_ty *mlp = mdlp->item[k]->messages; for (size_t 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; /* No filepos is smaller than any other filepos. */ int 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) { /* 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 (size_t 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); } }