*** 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
76 #ifndef FOR_DOXYGEN
77
78 #include <stdio.h>
79 #include <stdlib.h>
80 #include <string.h>
81
82 #include <m17n.h>
83 #include <m17n-misc.h>
84
85 #define VERSION "1.0"
86
87 /* Print all coding system names.  */
88
89 void
90 list_coding ()
91 {
92   MSymbol *codings;
93   int i, n;
94   char *name;
95   int len, clm;
96
97   n = mconv_list_codings (&codings);
98   clm = 0;
99   for (i = 0; i < n; i++)
100     {
101       name = msymbol_name (codings[i]);
102       len = strlen (name) + 1;
103       if (clm + len >= 80)
104         {
105           printf ("\n");
106           clm = 0;
107         }
108       printf (" %s", name);
109       clm += len;
110     }
111   printf ("\n");
112   free (codings);
113 }
114
115
116 /* Print the usage of this program (the name is PROG), and exit with
117    EXIT_CODE.  */
118
119 void
120 help_exit (char *prog, int exit_code)
121 {
122   char *p = prog;
123
124   while (*p)
125     if (*p++ == '/')
126       prog = p;
127
128   printf ("Usage: %s [ OPTION ... ] [ INFILE [ OUTFILE ] ]\n", prog);
129   printf ("Convert encoding of given files from one to another.\n");
130   printf ("  If INFILE is omitted, the input is taken from standard input.\n");
131   printf ("  If OUTFILE is omitted, the output is written to standard output.\n");
132   printf ("The following OPTIONs are available.\n");
133   printf ("  %-13s %s", "-f FROMCODE",
134           "FROMCODE is the encoding of INFILE (defaults to UTF-8).\n");
135   printf ("  %-13s %s", "-t TOCODE",
136           "TOCODE is the encoding of OUTFILE (defaults to UTF-8).\n");
137   printf ("  %-13s %s", "-k", "Do not stop conversion on error.\n");
138   printf ("  %-13s %s", "-s", "Suppress warnings.\n");
139   printf ("  %-13s %s", "-v", "Print progress information.\n");
140   printf ("  %-13s %s", "-l", "List available encodings.\n");
141   printf ("  %-13s %s", "--version", "Print version number.\n");
142   printf ("  %-13s %s", "-h, --help", "Print this message.\n");
143   exit (exit_code);
144 }
145
146
147 /* Check invalid bytes found in the last decoding.  Text property
148    Mcharset of such a byte is Mcharset_binary.  */
149
150 void
151 check_invalid_bytes (MText *mt)
152 {
153   int from = 0, to = 0;
154   int len = mtext_len (mt);
155   int first = 1;
156
157   while (to < len)
158     {
159       int n = mtext_prop_range (mt, Mcharset, from, NULL, &to, 1);
160       MSymbol charset
161         = n > 0 ? (MSymbol) mtext_get_prop (mt, from, Mcharset) : Mnil;
162
163       if (charset == Mcharset_binary)
164         {
165           if (first)
166             {
167               fprintf (stderr,
168                        "Invalid bytes (at each character position);\n");
169               first = 0;
170             }
171           for (; from < to; from++)
172             fprintf (stderr, " 0x%02X(%d)", mtext_ref_char (mt, from), from);
173         }
174       else
175         from = to;
176     }
177   if (! first)
178     fprintf (stderr, "\n");
179 }
180
181
182 /* Check unencoded characters in the last encoding.  Text property
183    Mcoding of such a character is Mnil.  */
184
185 void
186 check_unencoded_chars (MText *mt, int len)
187 {
188   int from = 0, to = 0;
189   int first = 1;
190
191   while (to < len)
192     {
193       int n = mtext_prop_range (mt, Mcoding, from, NULL, &to, 1);
194       MSymbol coding
195         = n > 0 ? (MSymbol) mtext_get_prop (mt, from, Mcoding) : Mnil;
196
197       if (coding == Mnil)
198         {
199           if (first)
200             {
201               fprintf (stderr,
202                        "Unencoded characters (at each character position):\n");
203               first = 0;
204             }
205           for (; from < to; from++)
206             fprintf (stderr, " 0x%02X(%d)", mtext_ref_char (mt, from), from);
207         }
208       else
209         from = to;
210     }
211   if (! first)
212     fprintf (stderr, "\n");
213 }
214
215
216 /* Format MSG by FMT and print the result to the stderr, and exit.  */
217
218 #define FATAL_ERROR(fmt, arg)   \
219   do {                          \
220     fprintf (stderr, fmt, arg); \
221     exit (1);                   \
222   } while (0)
223
224
225 int
226 main (int argc, char **argv)
227 {
228   int suppress_warning, verbose, continue_on_error;
229   MSymbol incode, outcode;
230   FILE *in, *out;
231   MText *mt;
232   MConverter *converter;
233   int i;
234
235   /* Initialize the m17n library.  */
236   M17N_INIT ();
237   if (merror_code != MERROR_NONE)
238     FATAL_ERROR ("%s\n", "Fail to initialize the m17n library.");
239
240   /* Default encodings are both UTF-8.  */
241   incode = outcode = Mcoding_utf_8;
242   /* By default, read from standard input and write to standard output. */
243   in = stdin, out = stdout;
244   /* By default, all these flags are 0.  */
245   suppress_warning = verbose = continue_on_error = 0;
246   /* Parse the command line arguments.  */
247   for (i = 1; i < argc; i++)
248     {
249       if (! strcmp (argv[i], "--help")
250                || ! strcmp (argv[i], "-h")
251                || ! strcmp (argv[i], "-?"))
252         help_exit (argv[0], 0);
253       else if (! strcmp (argv[i], "--version"))
254         {
255           printf ("mconv (m17n library) %s\n", VERSION);
256           printf ("Copyright (C) 2003 AIST, JAPAN\n");
257           exit (0);
258         }
259       else if (! strcmp (argv[i], "-l"))
260         {
261           list_coding ();
262           M17N_FINI ();
263           exit (0);
264         }
265       else if (! strcmp (argv[i], "-f"))
266         {
267           incode = mconv_resolve_coding (msymbol (argv[++i]));
268           if (incode == Mnil)
269             FATAL_ERROR ("Unknown encoding: %s\n", argv[i]);
270         }
271       else if (! strcmp (argv[i], "-t"))
272         {
273           outcode = mconv_resolve_coding (msymbol (argv[++i]));
274           if (outcode == Mnil)
275             FATAL_ERROR ("Unknown encoding: %s\n", argv[i]);
276         }
277       else if (! strcmp (argv[i], "-k"))
278         continue_on_error = 1;
279       else if (! strcmp (argv[i], "-s"))
280         suppress_warning = 1;
281       else if (! strcmp (argv[i], "-v"))
282         verbose = 1;
283       else if (argv[i][0] != '-')
284         {
285           if (in == stdin)
286             {
287               in = fopen (argv[i], "r");
288               if (! in)
289                 FATAL_ERROR ("Can't read the file %s\n", argv[i]);
290             }
291           else if (out == stdout)
292             {
293               out = fopen (argv[i], "w");
294               if (! out)
295                 FATAL_ERROR ("Can't write the file %s\n", argv[i]);
296             }
297           else
298             help_exit (argv[0], 1);
299         }
300       else
301         help_exit (argv[0], 1);
302     }
303
304   /* Create an M-text to store the decoded characters.  */
305   mt = mtext ();
306
307   /* Create a converter for decoding.  */
308   converter = mconv_stream_converter (incode, in);
309   /* Instead of doing strict decoding, we decode all input bytes at
310      once, and check invalid bytes later by the fuction
311      check_invalid_bytes.  */
312   converter->lenient = 1;
313
314   mconv_decode (converter, mt);
315
316   if (! suppress_warning)
317     check_invalid_bytes (mt);
318   if (verbose)
319     fprintf (stderr, "%d bytes (%s) decoded into %d characters,\n",
320              converter->nbytes, msymbol_name (incode), mtext_len (mt));
321
322   mconv_free_converter (converter);
323
324   /* Create a converter for encoding.  */
325   converter = mconv_stream_converter (outcode, out);
326   /* Instead of doing strict encoding, we encode all characters at
327      once, and check unencoded characters later by the fuction
328      check_unencoded_chars.  */
329   converter->lenient = 1;
330   converter->last_block = 1;
331   if (mconv_encode (converter, mt) < 0
332       && ! suppress_warning)
333     fprintf (stderr, "I/O error on writing\n");
334   if (! suppress_warning)
335     check_unencoded_chars (mt, converter->nchars);
336   if (verbose)
337     fprintf (stderr, "%d characters encoded into %d bytes (%s).\n",
338              converter->nchars, converter->nbytes, msymbol_name (outcode));
339
340   /* Clear away.  */
341   mconv_free_converter (converter);
342   m17n_object_unref (mt);
343   M17N_FINI ();
344   exit (0);
345 }
346 #endif /* not FOR_DOXYGEN */