From: Bruno Haible Date: Mon, 16 Oct 2006 12:21:42 +0000 (+0000) Subject: Format independent code for writing textual catalog files. X-Git-Tag: 0.16.x-branchpoint~84 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=315e38f347702334128e5e503f5634367a1bab3c;p=thirdparty%2Fgettext.git Format independent code for writing textual catalog files. --- diff --git a/gettext-tools/src/write-catalog.c b/gettext-tools/src/write-catalog.c new file mode 100644 index 000000000..ec9ce0329 --- /dev/null +++ b/gettext-tools/src/write-catalog.c @@ -0,0 +1,341 @@ +/* GNU gettext - internationalization aids + Copyright (C) 1995-1998, 2000-2006 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 2, 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, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +/* Specification. */ +#include "write-catalog.h" + +#include +#include +#include +#include +#include + +#include "fwriteerror.h" +#include "error-progname.h" +#include "xvasprintf.h" +#include "po-xerror.h" +#include "gettext.h" + +/* Our regular abbreviation. */ +#define _(str) gettext (str) + + +/* =========== 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) +{ + FILE *fp; + + /* 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 accomodate 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; + } + } + } + + /* Open the output file. */ + if (filename != NULL && strcmp (filename, "-") != 0 + && strcmp (filename, "/dev/stdout") != 0) + { + fp = fopen (filename, "w"); + 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; + /* xgettext:no-c-format */ + filename = _("standard output"); + } + + output_syntax->print (mdlp, fp, page_width, debug); + + /* 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, it is OK to + sort them as if we were in the C locale. And strcoll() in the C locale + is the same as strcmp(). */ + return strcmp (a->msgid, b->msgid); +} + + +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. */ + if (a->filepos_count == 0) + { + if (b->filepos_count != 0) + return -1; + } + if (b->filepos_count == 0) + return 1; + + /* 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, it is OK to + sort them as if we were in the C locale. And strcoll() in the C locale + is the same as strcmp(). */ + return strcmp (a->msgid, b->msgid); +} + + +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); + } +} diff --git a/gettext-tools/src/write-catalog.h b/gettext-tools/src/write-catalog.h new file mode 100644 index 000000000..bc157c75a --- /dev/null +++ b/gettext-tools/src/write-catalog.h @@ -0,0 +1,86 @@ +/* GNU gettext - internationalization aids + Copyright (C) 1995-1998, 2000-2003, 2006 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 2, 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, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#ifndef _WRITE_CATALOG_H +#define _WRITE_CATALOG_H + +#include +#include + +#include "message.h" + + +#ifdef __cplusplus +extern "C" { +#endif + + +/* This structure describes a textual catalog output format. */ +struct catalog_output_format +{ + /* Outputs a list of domains of messages to a file. */ + void (*print) (msgdomain_list_ty *mdlp, FILE *fp, size_t page_width, bool debug); + + /* Whether the print function requires the MDLP to be encoded in UTF-8 + encoding. */ + bool requires_utf8; + + /* Whether the format supports multiple domains in a single file. */ + bool supports_multiple_domains; + + /* Whether the format supports contexts. */ + bool supports_contexts; + + /* Whether the format supports plurals. */ + bool supports_plurals; + + /* Whether the PO file format is a suitable alternative output format for + this one. */ + bool alternative_is_po; + + /* Whether a Java class is a suitable alternative output format for this + one. */ + bool alternative_is_java_class; +}; + +typedef const struct catalog_output_format * catalog_output_format_ty; + +/* These functions set some parameters for use by 'msgdomain_list_print'. */ +extern void + message_page_width_set (size_t width); + +/* Output MDLP into a PO file with the given FILENAME, according to the + parameters set by the functions above. */ +extern void + msgdomain_list_print (msgdomain_list_ty *mdlp, + const char *filename, + catalog_output_format_ty output_syntax, + bool force, bool debug); + +/* Sort MDLP destructively according to the given criterion. */ +extern void + msgdomain_list_sort_by_msgid (msgdomain_list_ty *mdlp); +extern void + msgdomain_list_sort_by_filepos (msgdomain_list_ty *mdlp); + + +#ifdef __cplusplus +} +#endif + + +#endif /* _WRITE_CATALOG_H */