Imported Upstream version 0.18.3.2
[platform/upstream/gettext.git] / gettext-tools / src / recode-sr-latin.c
1 /* Recode Serbian text from Cyrillic to Latin script.
2    Copyright (C) 2006-2007, 2010, 2012 Free Software Foundation, Inc.
3    Written by Bruno Haible <bruno@clisp.org>, 2006.
4
5    This program is free software: you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation; either version 3 of the License, or
8    (at your option) any later version.
9
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
17
18 #ifdef HAVE_CONFIG_H
19 # include "config.h"
20 #endif
21
22 #include <errno.h>
23 #include <getopt.h>
24 #include <stdbool.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <locale.h>
28
29 #if HAVE_ICONV
30 #include <iconv.h>
31 #endif
32
33 #include "closeout.h"
34 #include "error.h"
35 #include "progname.h"
36 #include "relocatable.h"
37 #include "basename.h"
38 #include "xalloc.h"
39 #include "localcharset.h"
40 #include "c-strcase.h"
41 #include "xstriconv.h"
42 #include "filters.h"
43 #include "propername.h"
44 #include "gettext.h"
45
46 #define _(str) gettext (str)
47
48
49 /* Long options.  */
50 static const struct option long_options[] =
51 {
52   { "help", no_argument, NULL, 'h' },
53   { "version", no_argument, NULL, 'V' },
54   { NULL, 0, NULL, 0 }
55 };
56
57 /* Forward declaration of local functions.  */
58 static void usage (int status)
59 #if defined __GNUC__ && ((__GNUC__ == 2 && __GNUC_MINOR__ >= 5) || __GNUC__ > 2)
60      __attribute__ ((noreturn))
61 #endif
62 ;
63 static void process (FILE *stream);
64
65 int
66 main (int argc, char *argv[])
67 {
68   /* Default values for command line options.  */
69   bool do_help = false;
70   bool do_version = false;
71
72   int opt;
73
74   /* Set program name for message texts.  */
75   set_program_name (argv[0]);
76
77 #ifdef HAVE_SETLOCALE
78   /* Set locale via LC_ALL.  */
79   setlocale (LC_ALL, "");
80 #endif
81
82   /* Set the text message domain.  */
83   bindtextdomain (PACKAGE, relocate (LOCALEDIR));
84   textdomain (PACKAGE);
85
86   /* Ensure that write errors on stdout are detected.  */
87   atexit (close_stdout);
88
89   /* Parse command line options.  */
90   while ((opt = getopt_long (argc, argv, "hV", long_options, NULL)) != EOF)
91     switch (opt)
92     {
93     case '\0':          /* Long option.  */
94       break;
95     case 'h':
96       do_help = true;
97       break;
98     case 'V':
99       do_version = true;
100       break;
101     default:
102       usage (EXIT_FAILURE);
103     }
104
105   /* Version information is requested.  */
106   if (do_version)
107     {
108       printf ("%s (GNU %s) %s\n", basename (program_name), PACKAGE, VERSION);
109       /* xgettext: no-wrap */
110       printf (_("Copyright (C) %s Free Software Foundation, Inc.\n\
111 License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>\n\
112 This is free software: you are free to change and redistribute it.\n\
113 There is NO WARRANTY, to the extent permitted by law.\n\
114 "),
115               "2006-2007");
116       printf (_("Written by %s and %s.\n"),
117               /* TRANSLATORS: This is a proper name. The last name is
118                  (with Unicode escapes) "\u0160egan" or (with HTML entities)
119                  "&Scaron;egan".  */
120               proper_name_utf8 ("Danilo Segan", "Danilo \305\240egan"),
121               proper_name ("Bruno Haible"));
122       exit (EXIT_SUCCESS);
123     }
124
125   /* Help is requested.  */
126   if (do_help)
127     usage (EXIT_SUCCESS);
128
129   if (argc - optind > 0)
130     error (EXIT_FAILURE, 0, _("too many arguments"));
131
132   process (stdin);
133
134   exit (EXIT_SUCCESS);
135 }
136
137
138 /* Display usage information and exit.  */
139 static void
140 usage (int status)
141 {
142   if (status != EXIT_SUCCESS)
143     fprintf (stderr, _("Try '%s --help' for more information.\n"),
144              program_name);
145   else
146     {
147       /* xgettext: no-wrap */
148       printf (_("\
149 Usage: %s [OPTION]\n\
150 "), program_name);
151       printf ("\n");
152       /* xgettext: no-wrap */
153       printf (_("\
154 Recode Serbian text from Cyrillic to Latin script.\n"));
155       /* xgettext: no-wrap */
156       printf (_("\
157 The input text is read from standard input.  The converted text is output to\n\
158 standard output.\n"));
159       printf ("\n");
160       /* xgettext: no-wrap */
161       printf (_("\
162 Informative output:\n"));
163       /* xgettext: no-wrap */
164       printf (_("\
165   -h, --help                  display this help and exit\n"));
166       /* xgettext: no-wrap */
167       printf (_("\
168   -V, --version               output version information and exit\n"));
169       printf ("\n");
170       /* TRANSLATORS: The placeholder indicates the bug-reporting address
171          for this package.  Please add _another line_ saying
172          "Report translation bugs to <...>\n" with the address for translation
173          bugs (typically your translation team's web or email address).  */
174       fputs (_("Report bugs to <bug-gnu-gettext@gnu.org>.\n"), stdout);
175     }
176
177   exit (status);
178 }
179
180
181 /* Routines for reading a line.
182    Don't use routines that drop NUL bytes.  Don't use getline(), because it
183    doesn't provide a good error message in case of memory allocation failure.
184    The gnulib module 'linebuffer' is nearly the right thing, except that we
185    don't want an extra newline at the end of file.  */
186
187 /* A 'struct linebuffer' holds a line of text. */
188
189 struct linebuffer
190 {
191   size_t size;                  /* Allocated. */
192   size_t length;                /* Used. */
193   char *buffer;
194 };
195
196 /* Initialize linebuffer LINEBUFFER for use. */
197 static inline void
198 init_linebuffer (struct linebuffer *lb)
199 {
200   lb->size = 0;
201   lb->length = 0;
202   lb->buffer = NULL;
203 }
204
205 /* Read an arbitrarily long line of text from STREAM into linebuffer LB.
206    Keep the newline.  Do not NUL terminate.
207    Return LINEBUFFER, except at end of file return NULL.  */
208 static struct linebuffer *
209 read_linebuffer (struct linebuffer *lb, FILE *stream)
210 {
211   if (feof (stream))
212     return NULL;
213   else
214     {
215       char *p = lb->buffer;
216       char *end = lb->buffer + lb->size;
217
218       for (;;)
219         {
220           int c = getc (stream);
221           if (c == EOF)
222             {
223               if (p == lb->buffer || ferror (stream))
224                 return NULL;
225               break;
226             }
227           if (p == end)
228             {
229               size_t oldsize = lb->size; /* = p - lb->buffer */
230               size_t newsize = 2 * oldsize + 40;
231               lb->buffer = (char *) xrealloc (lb->buffer, newsize);
232               lb->size = newsize;
233               p = lb->buffer + oldsize;
234               end = lb->buffer + newsize;
235             }
236           *p++ = c;
237           if (c == '\n')
238             break;
239         }
240
241       lb->length = p - lb->buffer;
242       return lb;
243     }
244 }
245
246 /* Free linebuffer LB and its data, all allocated with malloc. */
247 static inline void
248 destroy_linebuffer (struct linebuffer *lb)
249 {
250   if (lb->buffer != NULL)
251     free (lb->buffer);
252 }
253
254
255 /* Process the input and produce the output.  */
256 static void
257 process (FILE *stream)
258 {
259   struct linebuffer lb;
260   const char *locale_code = locale_charset ();
261   bool need_code_conversion = (c_strcasecmp (locale_code, "UTF-8") != 0);
262 #if HAVE_ICONV
263   iconv_t conv_to_utf8 = (iconv_t)(-1);
264   iconv_t conv_from_utf8 = (iconv_t)(-1);
265   char *last_utf8_line;
266   size_t last_utf8_line_len;
267   char *last_backconv_line;
268   size_t last_backconv_line_len;
269 #endif
270
271   init_linebuffer (&lb);
272
273   /* Initialize the conversion descriptors.  */
274   if (need_code_conversion)
275     {
276 #if HAVE_ICONV
277       /* Avoid glibc-2.1 bug with EUC-KR.  */
278 # if ((__GLIBC__ == 2 && __GLIBC_MINOR__ <= 1) && !defined __UCLIBC__) \
279      && !defined _LIBICONV_VERSION
280       if (strcmp (locale_code, "EUC-KR") != 0)
281 # endif
282         {
283           conv_to_utf8 = iconv_open ("UTF-8", locale_code);
284           /* TODO:  Maybe append //TRANSLIT here?  */
285           conv_from_utf8 = iconv_open (locale_code, "UTF-8");
286         }
287       if (conv_to_utf8 == (iconv_t)(-1))
288         error (EXIT_FAILURE, 0, _("\
289 Cannot convert from \"%s\" to \"%s\". %s relies on iconv(), \
290 and iconv() does not support this conversion."),
291                locale_code, "UTF-8", basename (program_name));
292       if (conv_from_utf8 == (iconv_t)(-1))
293         error (EXIT_FAILURE, 0, _("\
294 Cannot convert from \"%s\" to \"%s\". %s relies on iconv(), \
295 and iconv() does not support this conversion."),
296                "UTF-8", locale_code, basename (program_name));
297       last_utf8_line = NULL;
298       last_utf8_line_len = 0;
299       last_backconv_line = NULL;
300       last_backconv_line_len = 0;
301 #else
302       error (EXIT_FAILURE, 0, _("\
303 Cannot convert from \"%s\" to \"%s\". %s relies on iconv(). \
304 This version was built without iconv()."),
305              locale_code, "UTF-8", basename (program_name));
306 #endif
307     }
308
309   /* Read the input line by line.
310      Processing it character by character is not possible, because some
311      filters need to look at adjacent characters.  Processing the entire file
312      in a whole chunk would take an excessive amount of memory.  */
313   for (;;)
314     {
315       char *line;
316       size_t line_len;
317       char *filtered_line;
318       size_t filtered_line_len;
319
320       /* Read a line.  */
321       if (read_linebuffer (&lb, stream) == NULL)
322         break;
323       line = lb.buffer;
324       line_len = lb.length;
325       /* read_linebuffer always returns a non-void result.  */
326       if (line_len == 0)
327         abort ();
328
329 #if HAVE_ICONV
330       /* Convert it to UTF-8.  */
331       if (need_code_conversion)
332         {
333           char *utf8_line = last_utf8_line;
334           size_t utf8_line_len = last_utf8_line_len;
335
336           if (xmem_cd_iconv (line, line_len, conv_to_utf8,
337                              &utf8_line, &utf8_line_len) != 0)
338             error (EXIT_FAILURE, errno,
339                    _("input is not valid in \"%s\" encoding"),
340                    locale_code);
341           if (utf8_line != last_utf8_line)
342             {
343               if (last_utf8_line != NULL)
344                 free (last_utf8_line);
345               last_utf8_line = utf8_line;
346               last_utf8_line_len = utf8_line_len;
347             }
348
349           line = utf8_line;
350           line_len = utf8_line_len;
351         }
352 #endif
353
354       /* Apply the filter.  */
355       serbian_to_latin (line, line_len, &filtered_line, &filtered_line_len);
356
357 #if HAVE_ICONV
358       /* Convert it back to the original encoding.  */
359       if (need_code_conversion)
360         {
361           char *backconv_line = last_backconv_line;
362           size_t backconv_line_len = last_backconv_line_len;
363
364           if (xmem_cd_iconv (filtered_line, filtered_line_len, conv_from_utf8,
365                              &backconv_line, &backconv_line_len) != 0)
366             error (EXIT_FAILURE, errno,
367                    _("error while converting from \"%s\" encoding to \"%s\" encoding"),
368                    "UTF-8", locale_code);
369           if (backconv_line != last_backconv_line)
370             {
371               if (last_backconv_line != NULL)
372                 free (last_backconv_line);
373               last_backconv_line = backconv_line;
374               last_backconv_line_len = backconv_line_len;
375             }
376
377           fwrite (backconv_line, 1, backconv_line_len, stdout);
378         }
379       else
380 #endif
381         fwrite (filtered_line, 1, filtered_line_len, stdout);
382
383       free (filtered_line);
384     }
385
386 #if HAVE_ICONV
387   if (need_code_conversion)
388     {
389       iconv_close (conv_from_utf8);
390       iconv_close (conv_to_utf8);
391     }
392 #endif
393
394   destroy_linebuffer (&lb);
395 }