*** empty log message ***
[m17n/m17n-lib.git] / example / mconv.c
1 /* mconv.c -- Code converter.
2    Copyright (C) 2003, 2004
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., 59 Temple Place, Suite 330, Boston, MA
21    02111-1307, USA.  */
22
23 /***en
24     @page mconv convert file code
25
26     @section mconv-synopsis SYNOPSIS
27
28     mconv [ OPTION ... ] [ INFILE [ OUTFILE ] ]
29
30     @section mconv-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     @page mconv ¥Õ¥¡¥¤¥ë¤Î¥³¡¼¥É¤òÊÑ´¹¤¹¤ë
77
78     @section mconv-synopsis SYNOPSIS
79
80     mconv [ OPTION ... ] [ INFILE [ OUTFILE ] ]
81
82     @section mconv-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 #define VERSION "1.0"
138
139 /* Print all coding system names.  */
140
141 void
142 list_coding ()
143 {
144   MSymbol *codings;
145   int i, n;
146   char *name;
147   int len, clm;
148
149   n = mconv_list_codings (&codings);
150   clm = 0;
151   for (i = 0; i < n; i++)
152     {
153       name = msymbol_name (codings[i]);
154       len = strlen (name) + 1;
155       if (clm + len >= 80)
156         {
157           printf ("\n");
158           clm = 0;
159         }
160       printf (" %s", name);
161       clm += len;
162     }
163   printf ("\n");
164   free (codings);
165 }
166
167
168 /* Print the usage of this program (the name is PROG), and exit with
169    EXIT_CODE.  */
170
171 void
172 help_exit (char *prog, int exit_code)
173 {
174   char *p = prog;
175
176   while (*p)
177     if (*p++ == '/')
178       prog = p;
179
180   printf ("Usage: %s [ OPTION ... ] [ INFILE [ OUTFILE ] ]\n", prog);
181   printf ("Convert encoding of given files from one to another.\n");
182   printf ("  If INFILE is omitted, the input is taken from standard input.\n");
183   printf ("  If OUTFILE is omitted, the output is written to standard output.\n");
184   printf ("The following OPTIONs are available.\n");
185   printf ("  %-13s %s", "-f FROMCODE",
186           "FROMCODE is the encoding of INFILE (defaults to UTF-8).\n");
187   printf ("  %-13s %s", "-t TOCODE",
188           "TOCODE is the encoding of OUTFILE (defaults to UTF-8).\n");
189   printf ("  %-13s %s", "-k", "Do not stop conversion on error.\n");
190   printf ("  %-13s %s", "-s", "Suppress warnings.\n");
191   printf ("  %-13s %s", "-v", "Print progress information.\n");
192   printf ("  %-13s %s", "-l", "List available encodings.\n");
193   printf ("  %-13s %s", "--version", "Print version number.\n");
194   printf ("  %-13s %s", "-h, --help", "Print this message.\n");
195   exit (exit_code);
196 }
197
198
199 /* Check invalid bytes found in the last decoding.  Text property
200    Mcharset of such a byte is Mcharset_binary.  */
201
202 void
203 check_invalid_bytes (MText *mt)
204 {
205   int from = 0, to = 0;
206   int len = mtext_len (mt);
207   int first = 1;
208
209   while (to < len)
210     {
211       int n = mtext_prop_range (mt, Mcharset, from, NULL, &to, 1);
212       MSymbol charset
213         = n > 0 ? (MSymbol) mtext_get_prop (mt, from, Mcharset) : Mnil;
214
215       if (charset == Mcharset_binary)
216         {
217           if (first)
218             {
219               fprintf (stderr,
220                        "Invalid bytes (at each character position);\n");
221               first = 0;
222             }
223           for (; from < to; from++)
224             fprintf (stderr, " 0x%02X(%d)", mtext_ref_char (mt, from), from);
225         }
226       else
227         from = to;
228     }
229   if (! first)
230     fprintf (stderr, "\n");
231 }
232
233
234 /* Check unencoded characters in the last encoding.  Text property
235    Mcoding of such a character is Mnil.  */
236
237 void
238 check_unencoded_chars (MText *mt, int len)
239 {
240   int from = 0, to = 0;
241   int first = 1;
242
243   while (to < len)
244     {
245       int n = mtext_prop_range (mt, Mcoding, from, NULL, &to, 1);
246       MSymbol coding
247         = n > 0 ? (MSymbol) mtext_get_prop (mt, from, Mcoding) : Mnil;
248
249       if (coding == Mnil)
250         {
251           if (first)
252             {
253               fprintf (stderr,
254                        "Unencoded characters (at each character position):\n");
255               first = 0;
256             }
257           for (; from < to; from++)
258             fprintf (stderr, " 0x%02X(%d)", mtext_ref_char (mt, from), from);
259         }
260       else
261         from = to;
262     }
263   if (! first)
264     fprintf (stderr, "\n");
265 }
266
267
268 /* Format MSG by FMT and print the result to the stderr, and exit.  */
269
270 #define FATAL_ERROR(fmt, arg)   \
271   do {                          \
272     fprintf (stderr, fmt, arg); \
273     exit (1);                   \
274   } while (0)
275
276
277 int
278 main (int argc, char **argv)
279 {
280   int suppress_warning, verbose, continue_on_error;
281   MSymbol incode, outcode;
282   FILE *in, *out;
283   MText *mt;
284   MConverter *converter;
285   int i;
286
287   /* Initialize the m17n library.  */
288   M17N_INIT ();
289   if (merror_code != MERROR_NONE)
290     FATAL_ERROR ("%s\n", "Fail to initialize the m17n library.");
291
292   /* Default encodings are both UTF-8.  */
293   incode = outcode = Mcoding_utf_8;
294   /* By default, read from standard input and write to standard output. */
295   in = stdin, out = stdout;
296   /* By default, all these flags are 0.  */
297   suppress_warning = verbose = continue_on_error = 0;
298   /* Parse the command line arguments.  */
299   for (i = 1; i < argc; i++)
300     {
301       if (! strcmp (argv[i], "--help")
302                || ! strcmp (argv[i], "-h")
303                || ! strcmp (argv[i], "-?"))
304         help_exit (argv[0], 0);
305       else if (! strcmp (argv[i], "--version"))
306         {
307           printf ("mconv (m17n library) %s\n", VERSION);
308           printf ("Copyright (C) 2003 AIST, JAPAN\n");
309           exit (0);
310         }
311       else if (! strcmp (argv[i], "-l"))
312         {
313           list_coding ();
314           M17N_FINI ();
315           exit (0);
316         }
317       else if (! strcmp (argv[i], "-f"))
318         {
319           incode = mconv_resolve_coding (msymbol (argv[++i]));
320           if (incode == Mnil)
321             FATAL_ERROR ("Unknown encoding: %s\n", argv[i]);
322         }
323       else if (! strcmp (argv[i], "-t"))
324         {
325           outcode = mconv_resolve_coding (msymbol (argv[++i]));
326           if (outcode == Mnil)
327             FATAL_ERROR ("Unknown encoding: %s\n", argv[i]);
328         }
329       else if (! strcmp (argv[i], "-k"))
330         continue_on_error = 1;
331       else if (! strcmp (argv[i], "-s"))
332         suppress_warning = 1;
333       else if (! strcmp (argv[i], "-v"))
334         verbose = 1;
335       else if (argv[i][0] != '-')
336         {
337           if (in == stdin)
338             {
339               in = fopen (argv[i], "r");
340               if (! in)
341                 FATAL_ERROR ("Can't read the file %s\n", argv[i]);
342             }
343           else if (out == stdout)
344             {
345               out = fopen (argv[i], "w");
346               if (! out)
347                 FATAL_ERROR ("Can't write the file %s\n", argv[i]);
348             }
349           else
350             help_exit (argv[0], 1);
351         }
352       else
353         help_exit (argv[0], 1);
354     }
355
356   /* Create an M-text to store the decoded characters.  */
357   mt = mtext ();
358
359   /* Create a converter for decoding.  */
360   converter = mconv_stream_converter (incode, in);
361   /* Instead of doing strict decoding, we decode all input bytes at
362      once, and check invalid bytes later by the fuction
363      check_invalid_bytes.  */
364   converter->lenient = 1;
365
366   mconv_decode (converter, mt);
367
368   if (! suppress_warning)
369     check_invalid_bytes (mt);
370   if (verbose)
371     fprintf (stderr, "%d bytes (%s) decoded into %d characters,\n",
372              converter->nbytes, msymbol_name (incode), mtext_len (mt));
373
374   mconv_free_converter (converter);
375
376   /* Create a converter for encoding.  */
377   converter = mconv_stream_converter (outcode, out);
378   /* Instead of doing strict encoding, we encode all characters at
379      once, and check unencoded characters later by the fuction
380      check_unencoded_chars.  */
381   converter->lenient = 1;
382   converter->last_block = 1;
383   if (mconv_encode (converter, mt) < 0
384       && ! suppress_warning)
385     fprintf (stderr, "I/O error on writing\n");
386   if (! suppress_warning)
387     check_unencoded_chars (mt, converter->nchars);
388   if (verbose)
389     fprintf (stderr, "%d characters encoded into %d bytes (%s).\n",
390              converter->nchars, converter->nbytes, msymbol_name (outcode));
391
392   /* Clear away.  */
393   mconv_free_converter (converter);
394   m17n_object_unref (mt);
395   M17N_FINI ();
396   exit (0);
397 }
398 #endif /* not FOR_DOXYGEN */