Home | History | Annotate | Line # | Download | only in src
      1 /* GNU gettext - internationalization aids
      2    Copyright (C) 1995-1998, 2000-2006 Free Software Foundation, Inc.
      3 
      4    This program is free software; you can redistribute it and/or modify
      5    it under the terms of the GNU General Public License as published by
      6    the Free Software Foundation; either version 2, or (at your option)
      7    any later version.
      8 
      9    This program is distributed in the hope that it will be useful,
     10    but WITHOUT ANY WARRANTY; without even the implied warranty of
     11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     12    GNU General Public License for more details.
     13 
     14    You should have received a copy of the GNU General Public License
     15    along with this program; if not, write to the Free Software Foundation,
     16    Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
     17 
     18 #ifdef HAVE_CONFIG_H
     19 # include <config.h>
     20 #endif
     21 
     22 /* Specification.  */
     23 #include "write-catalog.h"
     24 
     25 #include <errno.h>
     26 #include <limits.h>
     27 #include <stdio.h>
     28 #include <stdlib.h>
     29 #include <string.h>
     30 
     31 #include "fwriteerror.h"
     32 #include "error-progname.h"
     33 #include "xvasprintf.h"
     34 #include "po-xerror.h"
     35 #include "gettext.h"
     36 
     37 /* Our regular abbreviation.  */
     38 #define _(str) gettext (str)
     39 
     40 
     41 /* =========== Some parameters for use by 'msgdomain_list_print'. ========== */
     42 
     43 
     44 /* This variable controls the page width when printing messages.
     45    Defaults to PAGE_WIDTH if not set.  Zero (0) given to message_page_-
     46    width_set will result in no wrapping being performed.  */
     47 static size_t page_width = PAGE_WIDTH;
     48 
     49 void
     50 message_page_width_set (size_t n)
     51 {
     52   if (n == 0)
     53     {
     54       page_width = INT_MAX;
     55       return;
     56     }
     57 
     58   if (n < 20)
     59     n = 20;
     60 
     61   page_width = n;
     62 }
     63 
     64 
     65 /* ======================== msgdomain_list_print() ======================== */
     66 
     67 
     68 void
     69 msgdomain_list_print (msgdomain_list_ty *mdlp, const char *filename,
     70 		      catalog_output_format_ty output_syntax,
     71 		      bool force, bool debug)
     72 {
     73   FILE *fp;
     74 
     75   /* We will not write anything if, for every domain, we have no message
     76      or only the header entry.  */
     77   if (!force)
     78     {
     79       bool found_nonempty = false;
     80       size_t k;
     81 
     82       for (k = 0; k < mdlp->nitems; k++)
     83 	{
     84 	  message_list_ty *mlp = mdlp->item[k]->messages;
     85 
     86 	  if (!(mlp->nitems == 0
     87 		|| (mlp->nitems == 1 && is_header (mlp->item[0]))))
     88 	    {
     89 	      found_nonempty = true;
     90 	      break;
     91 	    }
     92 	}
     93 
     94       if (!found_nonempty)
     95 	return;
     96     }
     97 
     98   /* Check whether the output format can accomodate all messages.  */
     99   if (!output_syntax->supports_multiple_domains && mdlp->nitems > 1)
    100     {
    101       if (output_syntax->alternative_is_po)
    102 	po_xerror (PO_SEVERITY_FATAL_ERROR, NULL, NULL, 0, 0, false, _("\
    103 Cannot output multiple translation domains into a single file with the specified output format. Try using PO file syntax instead."));
    104       else
    105 	po_xerror (PO_SEVERITY_FATAL_ERROR, NULL, NULL, 0, 0, false, _("\
    106 Cannot output multiple translation domains into a single file with the specified output format."));
    107     }
    108   else
    109     {
    110       if (!output_syntax->supports_contexts)
    111 	{
    112 	  const lex_pos_ty *has_context;
    113 	  size_t k;
    114 
    115 	  has_context = NULL;
    116 	  for (k = 0; k < mdlp->nitems; k++)
    117 	    {
    118 	      message_list_ty *mlp = mdlp->item[k]->messages;
    119 	      size_t j;
    120 
    121 	      for (j = 0; j < mlp->nitems; j++)
    122 		{
    123 		  message_ty *mp = mlp->item[j];
    124 
    125 		  if (mp->msgctxt != NULL)
    126 		    {
    127 		      has_context = &mp->pos;
    128 		      break;
    129 		    }
    130 		}
    131 	    }
    132 
    133 	  if (has_context != NULL)
    134 	    {
    135 	      error_with_progname = false;
    136 	      po_xerror (PO_SEVERITY_FATAL_ERROR, NULL,
    137 			 has_context->file_name, has_context->line_number,
    138 			 (size_t)(-1), false, _("\
    139 message catalog has context dependent translations, but the output format does not support them."));
    140 	      error_with_progname = true;
    141 	    }
    142 	}
    143 
    144       if (!output_syntax->supports_plurals)
    145 	{
    146 	  const lex_pos_ty *has_plural;
    147 	  size_t k;
    148 
    149 	  has_plural = NULL;
    150 	  for (k = 0; k < mdlp->nitems; k++)
    151 	    {
    152 	      message_list_ty *mlp = mdlp->item[k]->messages;
    153 	      size_t j;
    154 
    155 	      for (j = 0; j < mlp->nitems; j++)
    156 		{
    157 		  message_ty *mp = mlp->item[j];
    158 
    159 		  if (mp->msgid_plural != NULL)
    160 		    {
    161 		      has_plural = &mp->pos;
    162 		      break;
    163 		    }
    164 		}
    165 	    }
    166 
    167 	  if (has_plural != NULL)
    168 	    {
    169 	      error_with_progname = false;
    170 	      if (output_syntax->alternative_is_java_class)
    171 		po_xerror (PO_SEVERITY_FATAL_ERROR, NULL,
    172 			   has_plural->file_name, has_plural->line_number,
    173 			   (size_t)(-1), false, _("\
    174 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."));
    175 	      else
    176 		po_xerror (PO_SEVERITY_FATAL_ERROR, NULL,
    177 			   has_plural->file_name, has_plural->line_number,
    178 			   (size_t)(-1), false, _("\
    179 message catalog has plural form translations, but the output format does not support them."));
    180 	      error_with_progname = true;
    181 	    }
    182 	}
    183     }
    184 
    185   /* Open the output file.  */
    186   if (filename != NULL && strcmp (filename, "-") != 0
    187       && strcmp (filename, "/dev/stdout") != 0)
    188     {
    189       fp = fopen (filename, "w");
    190       if (fp == NULL)
    191 	{
    192 	  const char *errno_description = strerror (errno);
    193 	  po_xerror (PO_SEVERITY_FATAL_ERROR, NULL, NULL, 0, 0, false,
    194 		     xasprintf ("%s: %s",
    195 				xasprintf (_("cannot create output file \"%s\""),
    196 					   filename),
    197 				errno_description));
    198 	}
    199     }
    200   else
    201     {
    202       fp = stdout;
    203       /* xgettext:no-c-format */
    204       filename = _("standard output");
    205     }
    206 
    207   output_syntax->print (mdlp, fp, page_width, debug);
    208 
    209   /* Make sure nothing went wrong.  */
    210   if (fwriteerror (fp))
    211     {
    212       const char *errno_description = strerror (errno);
    213       po_xerror (PO_SEVERITY_FATAL_ERROR, NULL, NULL, 0, 0, false,
    214 		 xasprintf ("%s: %s",
    215 			    xasprintf (_("error while writing \"%s\" file"),
    216 				       filename),
    217 			    errno_description));
    218     }
    219 }
    220 
    221 
    222 /* =============================== Sorting. ================================ */
    223 
    224 
    225 static int
    226 cmp_by_msgid (const void *va, const void *vb)
    227 {
    228   const message_ty *a = *(const message_ty **) va;
    229   const message_ty *b = *(const message_ty **) vb;
    230   /* Because msgids normally contain only ASCII characters, it is OK to
    231      sort them as if we were in the C locale. And strcoll() in the C locale
    232      is the same as strcmp().  */
    233   return strcmp (a->msgid, b->msgid);
    234 }
    235 
    236 
    237 void
    238 msgdomain_list_sort_by_msgid (msgdomain_list_ty *mdlp)
    239 {
    240   size_t k;
    241 
    242   for (k = 0; k < mdlp->nitems; k++)
    243     {
    244       message_list_ty *mlp = mdlp->item[k]->messages;
    245 
    246       if (mlp->nitems > 0)
    247 	qsort (mlp->item, mlp->nitems, sizeof (mlp->item[0]), cmp_by_msgid);
    248     }
    249 }
    250 
    251 
    252 /* Sort the file positions of every message.  */
    253 
    254 static int
    255 cmp_filepos (const void *va, const void *vb)
    256 {
    257   const lex_pos_ty *a = (const lex_pos_ty *) va;
    258   const lex_pos_ty *b = (const lex_pos_ty *) vb;
    259   int cmp;
    260 
    261   cmp = strcmp (a->file_name, b->file_name);
    262   if (cmp == 0)
    263     cmp = (int) a->line_number - (int) b->line_number;
    264 
    265   return cmp;
    266 }
    267 
    268 static void
    269 msgdomain_list_sort_filepos (msgdomain_list_ty *mdlp)
    270 {
    271   size_t j, k;
    272 
    273   for (k = 0; k < mdlp->nitems; k++)
    274     {
    275       message_list_ty *mlp = mdlp->item[k]->messages;
    276 
    277       for (j = 0; j < mlp->nitems; j++)
    278 	{
    279 	  message_ty *mp = mlp->item[j];
    280 
    281 	  if (mp->filepos_count > 0)
    282 	    qsort (mp->filepos, mp->filepos_count, sizeof (mp->filepos[0]),
    283 		   cmp_filepos);
    284 	}
    285     }
    286 }
    287 
    288 
    289 /* Sort the messages according to the file position.  */
    290 
    291 static int
    292 cmp_by_filepos (const void *va, const void *vb)
    293 {
    294   const message_ty *a = *(const message_ty **) va;
    295   const message_ty *b = *(const message_ty **) vb;
    296   int cmp;
    297 
    298   /* No filepos is smaller than any other filepos.  */
    299   if (a->filepos_count == 0)
    300     {
    301       if (b->filepos_count != 0)
    302 	return -1;
    303     }
    304   if (b->filepos_count == 0)
    305     return 1;
    306 
    307   /* Compare on the file names...  */
    308   cmp = strcmp (a->filepos[0].file_name, b->filepos[0].file_name);
    309   if (cmp != 0)
    310     return cmp;
    311 
    312   /* If they are equal, compare on the line numbers...  */
    313   cmp = a->filepos[0].line_number - b->filepos[0].line_number;
    314   if (cmp != 0)
    315     return cmp;
    316 
    317   /* If they are equal, compare on the msgid strings.  */
    318   /* Because msgids normally contain only ASCII characters, it is OK to
    319      sort them as if we were in the C locale. And strcoll() in the C locale
    320      is the same as strcmp().  */
    321   return strcmp (a->msgid, b->msgid);
    322 }
    323 
    324 
    325 void
    326 msgdomain_list_sort_by_filepos (msgdomain_list_ty *mdlp)
    327 {
    328   size_t k;
    329 
    330   /* It makes sense to compare filepos[0] of different messages only after
    331      the filepos[] array of each message has been sorted.  Sort it now.  */
    332   msgdomain_list_sort_filepos (mdlp);
    333 
    334   for (k = 0; k < mdlp->nitems; k++)
    335     {
    336       message_list_ty *mlp = mdlp->item[k]->messages;
    337 
    338       if (mlp->nitems > 0)
    339 	qsort (mlp->item, mlp->nitems, sizeof (mlp->item[0]), cmp_by_filepos);
    340     }
    341 }
    342