(main): Copyright years updated.
[m17n/m17n-lib.git] / example / mconv.c
1 /* mconv.c -- Code converter.                           -*- coding: euc-jp; -*-
2    Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008
3      National Institute of Advanced Industrial Science and Technology (AIST)
4      Registration Number H15PRO112
5
6    This file is part of the m17n library.
7
8    The m17n library is free software; you can redistribute it and/or
9    modify it under the terms of the GNU Lesser General Public License
10    as published by the Free Software Foundation; either version 2.1 of
11    the License, or (at your option) any later version.
12
13    The m17n library is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16    Lesser General Public License for more details.
17
18    You should have received a copy of the GNU Lesser General Public
19    License along with the m17n library; if not, write to the Free
20    Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21    02111-1307, USA.  */
22
23 /***en
24     @enpage m17n-conv convert file code
25
26     @section m17n-conv-synopsis SYNOPSIS
27
28     m17n-conv [ OPTION ... ] [ INFILE [ OUTFILE ] ]
29
30     @section m17n-conv-description DESCRIPTION
31
32     Convert encoding of given files from one to another.
33
34     If INFILE is omitted, the input is taken from standard input.  If
35     OUTFILE is omitted, the output written to standard output.
36
37     The following OPTIONs are available.
38
39     <ul>
40
41     <li> -f FROMCODE
42
43     FROMCODE is the encoding of INFILE (defaults to UTF-8).
44
45     <li> -t TOCODE
46
47     TOCODE is the encoding of OUTFILE (defaults to UTF-8).
48
49     <li> -k
50
51     Do not stop conversion on error.
52
53     <li> -s
54
55     Suppress warnings.
56
57     <li> -v
58
59     Print progress information.
60
61     <li> -l
62
63     List available encodings.
64
65     <li> --version
66
67     Print version number.
68
69     <li> -h, --help
70
71     Print this message.
72
73     </ul>
74 */
75 /***ja
76     @japage m17n-conv ¥Õ¥¡¥¤¥ë¤Î¥³¡¼¥É¤òÊÑ´¹¤¹¤ë
77
78     @section m17n-conv-synopsis SYNOPSIS
79
80     m17n-conv [ OPTION ... ] [ INFILE [ OUTFILE ] ]
81
82     @section m17n-conv-description ÀâÌÀ
83
84     Í¿¤¨¤é¤ì¤¿¥Õ¥¡¥¤¥ë¤Î¥³¡¼¥É¤òÊ̤Τâ¤Î¤ËÊÑ´¹¤¹¤ë¡£ 
85
86     INFILE ¤¬¾Êά¤µ¤ì¤¿¾ì¹ç¤Ï¡¢É¸½àÆþÎϤ«¤é¤È¤ë¡£OUTFILE ¤¬¾Êά¤µ¤ì¤¿ 
87     ¾ì¹ç¤Ï¡¢É¸½à½ÐÎϤؽñ¤­½Ð¤¹¡£
88
89     °Ê²¼¤Î¥ª¥×¥·¥ç¥ó¤¬ÍøÍѤǤ­¤ë¡£
90
91     <ul>
92
93     <li> -f FROMCODE
94
95     FROMCODE ¤Ï INFILE ¤Î¥³¡¼¥É·Ï¤Ç¤¢¤ë¡£(¥Ç¥Õ¥©¥ë¥È¤Ï UTF-8) 
96
97     <li> -t TOCODE
98
99     TOCODE ¤Ï OUTFILE ¤Î¥³¡¼¥É·Ï¤Ç¤¢¤ë¡£(¥Ç¥Õ¥©¥ë¥È¤Ï UTF-8) 
100
101     <li> -k
102
103     ¥¨¥é¡¼¤ÇÊÑ´¹¤òÄä»ß¤·¤Ê¤¤¡£
104
105     <li> -s
106
107     ·Ù¹ð¤òɽ¼¨¤·¤Ê¤¤¡£ 
108
109     <li> -v
110
111     ¿Ê¹Ô¾õ¶·¤òɽ¼¨¤¹¤ë¡£ 
112
113     <li> -l
114
115     ÍøÍѲÄǽ¤Ê¥³¡¼¥É·Ï¤òÎóµó¤¹¤ë¡£ 
116
117     <li> --version
118
119     ¥Ð¡¼¥¸¥ç¥óÈÖ¹æ¤òɽ¼¨¤¹¤ë¡£ 
120
121     <li> -h, --help
122
123     ¤³¤Î¥á¥Ã¥»¡¼¥¸¤òɽ¼¨¤¹¤ë¡£ 
124
125     </ul>
126 */
127
128 #ifndef FOR_DOXYGEN
129
130 #include <stdio.h>
131 #include <stdlib.h>
132 #include <string.h>
133
134 #include <m17n.h>
135 #include <m17n-misc.h>
136
137 /* Print all coding system names.  */
138
139 int
140 compare_coding_name (const void *elt1, const void *elt2)
141 {
142   const MSymbol *n1 = elt1;
143   const MSymbol *n2 = elt2;
144
145   return strcmp (msymbol_name (*n1), msymbol_name (*n2));
146 }
147
148 void
149 list_coding ()
150 {
151   MSymbol *codings;
152   int i, n;
153   char *name;
154   int len, clm;
155
156   n = mconv_list_codings (&codings);
157   qsort (codings, n, sizeof (MSymbol), compare_coding_name);
158   clm = 0;
159   for (i = 0; i < n; i++)
160     {
161       name = msymbol_name (codings[i]);
162       len = strlen (name) + 1;
163       if (clm + len >= 80)
164         {
165           printf ("\n");
166           clm = 0;
167         }
168       printf (" %s", name);
169       clm += len;
170     }
171   printf ("\n");
172   free (codings);
173 }
174
175
176 /* Print the usage of this program (the name is PROG), and exit with
177    EXIT_CODE.  */
178
179 void
180 help_exit (char *prog, int exit_code)
181 {
182   char *p = prog;
183
184   while (*p)
185     if (*p++ == '/')
186       prog = p;
187
188   printf ("Usage: %s [ OPTION ... ] [ INFILE [ OUTFILE ] ]\n", prog);
189   printf ("Convert encoding of given files from one to another.\n");
190   printf ("  If INFILE is omitted, the input is taken from standard input.\n");
191   printf ("  If OUTFILE is omitted, the output is written to standard output.\n");
192   printf ("The following OPTIONs are available.\n");
193   printf ("  %-13s %s", "-f FROMCODE",
194           "FROMCODE is the encoding of INFILE (defaults to UTF-8).\n");
195   printf ("  %-13s %s", "-t TOCODE",
196           "TOCODE is the encoding of OUTFILE (defaults to UTF-8).\n");
197   printf ("  %-13s %s", "-k", "Do not stop conversion on error.\n");
198   printf ("  %-13s %s", "-s", "Suppress warnings.\n");
199   printf ("  %-13s %s", "-v", "Print progress information.\n");
200   printf ("  %-13s %s", "-l", "List available encodings.\n");
201   printf ("  %-13s %s", "--version", "Print version number.\n");
202   printf ("  %-13s %s", "-h, --help", "Print this message.\n");
203   exit (exit_code);
204 }
205
206
207 /* Check invalid bytes found in the last decoding.  Text property
208    Mcharset of such a byte is Mcharset_binary.  */
209
210 void
211 check_invalid_bytes (MText *mt)
212 {
213   int from = 0, to = 0;
214   int len = mtext_len (mt);
215   int first = 1;
216
217   while (to < len)
218     {
219       int n = mtext_prop_range (mt, Mcharset, from, NULL, &to, 1);
220       MSymbol charset
221         = n > 0 ? (MSymbol) mtext_get_prop (mt, from, Mcharset) : Mnil;
222
223       if (charset == Mcharset_binary)
224         {
225           if (first)
226             {
227               fprintf (stderr,
228                        "Invalid bytes (at each character position);\n");
229               first = 0;
230             }
231           for (; from < to; from++)
232             fprintf (stderr, " 0x%02X(%d)", mtext_ref_char (mt, from), from);
233         }
234       else
235         from = to;
236     }
237   if (! first)
238     fprintf (stderr, "\n");
239 }
240
241
242 /* Check unencoded characters in the last encoding.  Text property
243    Mcoding of such a character is Mnil.  */
244
245 void
246 check_unencoded_chars (MText *mt, int len)
247 {
248   int from = 0, to = 0;
249   int first = 1;
250
251   while (to < len)
252     {
253       int n = mtext_prop_range (mt, Mcoding, from, NULL, &to, 1);
254       MSymbol coding
255         = n > 0 ? (MSymbol) mtext_get_prop (mt, from, Mcoding) : Mnil;
256
257       if (coding == Mnil)
258         {
259           if (first)
260             {
261               fprintf (stderr,
262                        "Unencoded characters (at each character position):\n");
263               first = 0;
264             }
265           for (; from < to; from++)
266             fprintf (stderr, " 0x%02X(%d)", mtext_ref_char (mt, from), from);
267         }
268       else
269         from = to;
270     }
271   if (! first)
272     fprintf (stderr, "\n");
273 }
274
275
276 /* Format MSG by FMT and print the result to the stderr, and exit.  */
277
278 #define FATAL_ERROR(fmt, arg)   \
279   do {                          \
280     fprintf (stderr, fmt, arg); \
281     exit (1);                   \
282   } while (0)
283
284
285 int
286 main (int argc, char **argv)
287 {
288   int suppress_warning, verbose, continue_on_error;
289   MSymbol incode, outcode;
290   FILE *in, *out;
291   MText *mt;
292   MConverter *converter;
293   int i;
294
295   /* Initialize the m17n library.  */
296   M17N_INIT ();
297   if (merror_code != MERROR_NONE)
298     FATAL_ERROR ("%s\n", "Fail to initialize the m17n library.");
299
300   /* Default encodings are both UTF-8.  */
301   incode = outcode = Mcoding_utf_8;
302   /* By default, read from standard input and write to standard output. */
303   in = stdin, out = stdout;
304   /* By default, all these flags are 0.  */
305   suppress_warning = verbose = continue_on_error = 0;
306   /* Parse the command line arguments.  */
307   for (i = 1; i < argc; i++)
308     {
309       if (! strcmp (argv[i], "--help")
310                || ! strcmp (argv[i], "-h")
311                || ! strcmp (argv[i], "-?"))
312         help_exit (argv[0], 0);
313       else if (! strcmp (argv[i], "--version"))
314         {
315           printf ("m17n-conv (m17n library) %s\n", M17NLIB_VERSION_NAME);
316           printf ("Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 AIST, JAPAN\n");
317           exit (0);
318         }
319       else if (! strcmp (argv[i], "-l"))
320         {
321           list_coding ();
322           M17N_FINI ();
323           exit (0);
324         }
325       else if (! strcmp (argv[i], "-f"))
326         {
327           incode = mconv_resolve_coding (msymbol (argv[++i]));
328           if (incode == Mnil)
329             FATAL_ERROR ("Unknown encoding: %s\n", argv[i]);
330         }
331       else if (! strcmp (argv[i], "-t"))
332         {
333           outcode = mconv_resolve_coding (msymbol (argv[++i]));
334           if (outcode == Mnil)
335             FATAL_ERROR ("Unknown encoding: %s\n", argv[i]);
336         }
337       else if (! strcmp (argv[i], "-k"))
338         continue_on_error = 1;
339       else if (! strcmp (argv[i], "-s"))
340         suppress_warning = 1;
341       else if (! strcmp (argv[i], "-v"))
342         verbose = 1;
343       else if (argv[i][0] != '-')
344         {
345           if (in == stdin)
346             {
347               in = fopen (argv[i], "r");
348               if (! in)
349                 FATAL_ERROR ("Can't read the file %s\n", argv[i]);
350             }
351           else if (out == stdout)
352             {
353               out = fopen (argv[i], "w");
354               if (! out)
355                 FATAL_ERROR ("Can't write the file %s\n", argv[i]);
356             }
357           else
358             help_exit (argv[0], 1);
359         }
360       else
361         help_exit (argv[0], 1);
362     }
363
364   /* Create an M-text to store the decoded characters.  */
365   mt = mtext ();
366
367   /* Create a converter for decoding.  */
368   converter = mconv_stream_converter (incode, in);
369   /* Instead of doing strict decoding, we decode all input bytes at
370      once, and check invalid bytes later by the fuction
371      check_invalid_bytes.  */
372   converter->lenient = 1;
373
374   mconv_decode (converter, mt);
375
376   if (! suppress_warning)
377     check_invalid_bytes (mt);
378   if (verbose)
379     fprintf (stderr, "%d bytes (%s) decoded into %d characters,\n",
380              converter->nbytes, msymbol_name (incode), mtext_len (mt));
381
382   mconv_free_converter (converter);
383
384   /* Create a converter for encoding.  */
385   converter = mconv_stream_converter (outcode, out);
386   /* Instead of doing strict encoding, we encode all characters at
387      once, and check unencoded characters later by the fuction
388      check_unencoded_chars.  */
389   converter->lenient = 1;
390   converter->last_block = 1;
391   if (mconv_encode (converter, mt) < 0
392       && ! suppress_warning)
393     fprintf (stderr, "I/O error on writing\n");
394   if (! suppress_warning)
395     check_unencoded_chars (mt, converter->nchars);
396   if (verbose)
397     fprintf (stderr, "%d characters encoded into %d bytes (%s).\n",
398              converter->nchars, converter->nbytes, msymbol_name (outcode));
399
400   /* Clear away.  */
401   mconv_free_converter (converter);
402   fclose (in);
403   fclose (out);
404   m17n_object_unref (mt);
405   M17N_FINI ();
406   exit (0);
407 }
408 #endif /* not FOR_DOXYGEN */