Imported Upstream version 0.19.7
[platform/upstream/gettext.git] / gettext-tools / src / write-tcl.c
1 /* Writing tcl/msgcat .msg files.
2    Copyright (C) 2002-2003, 2005, 2007-2009, 2015 Free Software
3    Foundation, Inc.
4    Written by Bruno Haible <bruno@clisp.org>, 2002.
5
6    This program is free software: you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 3 of the License, or
9    (at your option) any later version.
10
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15
16    You should have received a copy of the GNU General Public License
17    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
18
19 #ifdef HAVE_CONFIG_H
20 # include <config.h>
21 #endif
22 #include <alloca.h>
23
24 /* Specification.  */
25 #include "write-tcl.h"
26
27 #include <errno.h>
28 #include <stdbool.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32
33 #include "error.h"
34 #include "xerror.h"
35 #include "message.h"
36 #include "msgl-iconv.h"
37 #include "po-charset.h"
38 #include "xalloc.h"
39 #include "xmalloca.h"
40 #include "concat-filename.h"
41 #include "fwriteerror.h"
42 #include "unistr.h"
43 #include "gettext.h"
44
45 #define _(str) gettext (str)
46
47
48 /* Write a string in Tcl Unicode notation to the given stream.
49    Tcl 8 uses Unicode for its internal string representation.
50    In tcl-8.3.3, the .msg files are read in using the locale dependent
51    encoding.  The only way to specify strings in an encoding independent
52    form is the \unnnn notation.  Newer tcl versions have this fixed:
53    they read the .msg files in UTF-8 encoding.  */
54 static void
55 write_tcl_string (FILE *stream, const char *str)
56 {
57   static const char hexdigit[] = "0123456789abcdef";
58   const char *str_limit = str + strlen (str);
59
60   fprintf (stream, "\"");
61   while (str < str_limit)
62     {
63       ucs4_t uc;
64       unsigned int count;
65       count = u8_mbtouc (&uc, (const unsigned char *) str, str_limit - str);
66       if (uc < 0x10000)
67         {
68           /* Single UCS-2 'char'.  */
69           if (uc == 0x000a)
70             fprintf (stream, "\\n");
71           else if (uc == 0x000d)
72             fprintf (stream, "\\r");
73           else if (uc == 0x0022)
74             fprintf (stream, "\\\"");
75           else if (uc == 0x0024)
76             fprintf (stream, "\\$");
77           else if (uc == 0x005b)
78             fprintf (stream, "\\[");
79           else if (uc == 0x005c)
80             fprintf (stream, "\\\\");
81           else if (uc == 0x005d)
82             fprintf (stream, "\\]");
83           /* No need to escape '{' and '}' because we don't have opening
84              braces outside the strings.  */
85 #if 0
86           else if (uc == 0x007b)
87             fprintf (stream, "\\{");
88           else if (uc == 0x007d)
89             fprintf (stream, "\\}");
90 #endif
91           else if (uc >= 0x0020 && uc < 0x007f)
92             fprintf (stream, "%c", (int) uc);
93           else
94             fprintf (stream, "\\u%c%c%c%c",
95                      hexdigit[(uc >> 12) & 0x0f], hexdigit[(uc >> 8) & 0x0f],
96                      hexdigit[(uc >> 4) & 0x0f], hexdigit[uc & 0x0f]);
97         }
98       else
99         /* The \unnnn notation doesn't support characters >= 0x10000.
100            We output them as UTF-8 byte sequences and hope that either
101            the Tcl version reading them will be new enough or that the
102            user is using an UTF-8 locale.  */
103         fwrite (str, 1, count, stream);
104       str += count;
105     }
106   fprintf (stream, "\"");
107 }
108
109
110 static void
111 write_msg (FILE *output_file, message_list_ty *mlp, const char *locale_name)
112 {
113   size_t j;
114
115   /* We don't care about esthetic formattic of the output (like respecting
116      a maximum line width, or including the translator comments) because
117      the \unnnn notation is unesthetic anyway.  Translators shall edit
118      the PO file.  */
119   for (j = 0; j < mlp->nitems; j++)
120     {
121       message_ty *mp = mlp->item[j];
122
123       if (is_header (mp))
124         /* Tcl's msgcat unit ignores this, but msgunfmt needs it.  */
125         fprintf (output_file, "set ::msgcat::header ");
126       else
127         {
128           fprintf (output_file, "::msgcat::mcset %s ", locale_name);
129           write_tcl_string (output_file, mp->msgid);
130           fprintf (output_file, " ");
131         }
132       write_tcl_string (output_file, mp->msgstr);
133       fprintf (output_file, "\n");
134     }
135 }
136
137 int
138 msgdomain_write_tcl (message_list_ty *mlp, const char *canon_encoding,
139                      const char *locale_name,
140                      const char *directory)
141 {
142   /* If no entry for this domain don't even create the file.  */
143   if (mlp->nitems == 0)
144     return 0;
145
146   /* Determine whether mlp has entries with context.  */
147   {
148     bool has_context;
149     size_t j;
150
151     has_context = false;
152     for (j = 0; j < mlp->nitems; j++)
153       if (mlp->item[j]->msgctxt != NULL)
154         has_context = true;
155     if (has_context)
156       {
157         multiline_error (xstrdup (""),
158                          xstrdup (_("\
159 message catalog has context dependent translations\n\
160 but the Tcl message catalog format doesn't support contexts\n")));
161         return 1;
162       }
163   }
164
165   /* Determine whether mlp has plural entries.  */
166   {
167     bool has_plural;
168     size_t j;
169
170     has_plural = false;
171     for (j = 0; j < mlp->nitems; j++)
172       if (mlp->item[j]->msgid_plural != NULL)
173         has_plural = true;
174     if (has_plural)
175       {
176         multiline_error (xstrdup (""),
177                          xstrdup (_("\
178 message catalog has plural form translations\n\
179 but the Tcl message catalog format doesn't support plural handling\n")));
180         return 1;
181       }
182   }
183
184   /* Convert the messages to Unicode.  */
185   iconv_message_list (mlp, canon_encoding, po_charset_utf8, NULL);
186
187   /* Now create the file.  */
188   {
189     size_t len;
190     char *frobbed_locale_name;
191     char *p;
192     char *file_name;
193     FILE *output_file;
194
195     /* Convert the locale name to lowercase and remove any encoding.  */
196     len = strlen (locale_name);
197     frobbed_locale_name = (char *) xmalloca (len + 1);
198     memcpy (frobbed_locale_name, locale_name, len + 1);
199     for (p = frobbed_locale_name; *p != '\0'; p++)
200       if (*p >= 'A' && *p <= 'Z')
201         *p = *p - 'A' + 'a';
202       else if (*p == '.')
203         {
204           *p = '\0';
205           break;
206         }
207
208     file_name = xconcatenated_filename (directory, frobbed_locale_name, ".msg");
209
210     output_file = fopen (file_name, "w");
211     if (output_file == NULL)
212       {
213         error (0, errno, _("error while opening \"%s\" for writing"),
214                file_name);
215         freea (frobbed_locale_name);
216         return 1;
217       }
218
219     write_msg (output_file, mlp, frobbed_locale_name);
220
221     /* Make sure nothing went wrong.  */
222     if (fwriteerror (output_file))
223       error (EXIT_FAILURE, errno, _("error while writing \"%s\" file"),
224              file_name);
225
226     freea (frobbed_locale_name);
227   }
228
229   return 0;
230 }